From pa.dec.com!decwrl!sdd.hp.com!mips!samsung!uunet!sparky!kent Sun Aug 11 15:30:50 PDT 1991
Article: 2570 of comp.sources.misc
Path: pa.dec.com!decwrl!sdd.hp.com!mips!samsung!uunet!sparky!kent
From: wht@n4hgf.Mt-Park.GA.US (Warren Tucker)
Newsgroups: comp.sources.misc
Subject: v21i057:  ecu - ECU async comm package rev 3.10, Part05/37
Message-ID: <1991Aug3.033655.28223@sparky.IMD.Sterling.COM>
Date: 3 Aug 91 03:36:55 GMT
References: <csm-v21i053=ecu.215539@sparky.imd.sterling.com>
Sender: kent@sparky.IMD.Sterling.COM (Kent Landfield)
Organization: Sterling Software, IMD
Lines: 2204
Approved: kent@sparky.imd.sterling.com
X-Md4-Signature: 3407f1d6d9c88d72d96cccd1b9a44883

Submitted-by: Warren Tucker <wht@n4hgf.Mt-Park.GA.US>
Posting-number: Volume 21, Issue 57
Archive-name: ecu/part05
Supersedes: ecu3: Volume 16, Issue 25-59
Environment: SCO, XENIX, ISC

---- Cut Here and feed the following to sh ----
#!/bin/sh
# this is ecu310.05 (part 5 of ecu310)
# do not concatenate these parts, unpack them in order with /bin/sh
# file ecufkey.c continued
#
if touch 2>&1 | fgrep 'amc' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 5; then
	echo Please unpack part "$Scheck" next!
	exit 1
 else
	exit 0
 fi
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
	echo 'x - still skipping ecufkey.c'
else
echo 'x - continuing file ecufkey.c'
sed 's/^X//' << 'SHAR_EOF' >> 'ecufkey.c' &&
Xextern char curr_dir[CURR_DIRSIZ];	/* current working key defns */
X
XKDE		keyset_table[KDE_COUNT];
Xchar keyset_name[32] = "";
X
XXF_KDE_NAME xf_kde_name[] =
X{
X	{ XFcurup,	KDEk_CUU,	"CUU" },
X	{ XFcurdn,	KDEk_CUD,	"CUD" },
X	{ XFcurrt,	KDEk_CUR,	"CUR" },
X	{ XFcurlf,	KDEk_CUL,	"CUL" },
X	{ XFcur5,	KDEk_CU5,	"CU5" },
X	{ XFend,	KDEk_END,	"End" },
X	{ XFpgdn,	KDEk_PGDN,	"PgDn" },
X	{ XFhome,	KDEk_HOME,	"Home" },
X	{ XFpgup,	KDEk_PGUP,	"PgUp" },
X	{ XFins,	KDEk_INS,	"Ins" },
X	{ XF1,		KDEk_F1,	"F1" },
X	{ XF2,		KDEk_F2,	"F2" },
X	{ XF3,		KDEk_F3,	"F3" },
X	{ XF4,		KDEk_F4,	"F4" },
X	{ XF5,		KDEk_F5,	"F5" },
X	{ XF6,		KDEk_F6,	"F6" },
X	{ XF7,		KDEk_F7,	"F7" },
X	{ XF8,		KDEk_F8,	"F8" },
X	{ XF9,		KDEk_F9,	"F9" },
X	{ XF10,		KDEk_F10,	"F10" },
X	{ XF11,		KDEk_F11,	"F11" },
X	{ XF12,		KDEk_F12,	"F12" },
X	{ XFbktab,	KDEk_BKTAB,	"BkTab" },
X	{ 255,		KDEk_InitStr,"IS"},
X	{ 0,0,"" }
X};
X
X/*+-------------------------------------------------------------------------
X	keyset_init()
X--------------------------------------------------------------------------*/
Xvoid
Xkeyset_init()
X{
Xregister itmp;
Xregister KDE *tkde;
Xchar *make_char_graphic();
X
X	for(itmp = 0; itmp < KDE_COUNT; itmp++)
X	{
X		tkde = &keyset_table[itmp];
X		tkde->logical[0] = 0;
X		tkde->count = 0;
X		tkde->KDEt = (uchar)itmp;
X	}
X
X	keyset_name[0] = 0;
X
X	tkde = &keyset_table[KDEk_BKTAB];
X	strcpy(tkde->logical,"redisplay");
X	tkde->count = KACT_REDISPLAY;
X
X	tkde = &keyset_table[KDEk_HOME];
X	strcpy(tkde->logical,"ecu cmd");
X	tkde->count = KACT_COMMAND;
X
X	tkde = &keyset_table[KDEk_INS];
X	strcpy(tkde->logical,"local shell");
X	tkde->count = KACT_LOCAL_SHELL;
X
X	tkde = &keyset_table[KDEk_CU5];
X	strcpy(tkde->logical,"screen dump");
X	tkde->str[0] = 0x7F;	/* this key is intercepted by kbd read routine */
X	tkde->count = 0;
X
X}	/* end of keyset_init */
X
X/*+-------------------------------------------------------------------------
X	kde_fgets(buf,bufsize,fp)
X--------------------------------------------------------------------------*/
Xint
Xkde_fgets(buf,bufsize,fp)
Xchar *buf;
Xint bufsize;
XFILE *fp;
X{
Xregister itmp;
Xregister char *cptr;
X
X	if(!fgets(buf,bufsize,fp))
X	{
X		return(KDEt_EOF);
X	}
X
X	if(!(itmp = strlen(buf)))
X		return(KDEt_COMMENT);
X	if(buf[itmp - 1] == '\n')
X	{
X		buf[itmp - 1] = 0;
X		itmp--;
X	}
X	if(!itmp)
X		return(KDEt_COMMENT);
X
X	cptr = buf;
X	itmp = (strchr(" \t",*cptr)) ? KDEt_ENTRY : KDEt_NAME;
X	while(*cptr && ((*cptr == SPACE) || (*cptr == TAB)))
X		cptr++;
X
X	if(!*cptr || (*cptr == '#'))
X		return(KDEt_COMMENT);
X
X	return(itmp);
X	
X}	/* end of kde_fgets */
X
X/*+-------------------------------------------------------------------------
X	keyset_idnum(keystr)
X--------------------------------------------------------------------------*/
Xint
Xkeyset_idnum(keystr)
Xchar *keystr;
X{
Xstruct XF_KDE_NAME *xkn = xf_kde_name;
X	while(xkn->xf != 0)
X	{
X		if(!strcmpi(xkn->name,keystr))
X			return((int)xkn->kde);
X		xkn++;
X	}
X	return(-1);
X}	/* end of keyset_idnum */
X
X/*+-------------------------------------------------------------------------
X	keyset_idstr(KDEt)
X--------------------------------------------------------------------------*/
Xchar *
Xkeyset_idstr(KDEt)
Xint KDEt;
X{
Xstruct XF_KDE_NAME *xkn = xf_kde_name;
X
X	while(xkn->xf)
X	{
X		if((int)xkn->kde == KDEt)
X			return(xkn->name);
X		xkn++;
X	}
X	return((char *)0);
X}	/* end of keyset_idstr */
X
X/*+-------------------------------------------------------------------------
X	xf_to_KDEt(xf)
X--------------------------------------------------------------------------*/
Xint
Xxf_to_KDEt(xf)
Xuchar xf;
X{
Xstruct XF_KDE_NAME *xkn = xf_kde_name;
X
X	while(xkn->xf)
X	{
X		if(xkn->xf == xf)
X			return((int)xkn->kde & 0xFF);
X		xkn++;
X	}
X	return(-1);
X}	/* end of xf_to_KDEt */
X
X/*+-------------------------------------------------------------------------
X	keyset_define_key(bufptr)
X
Xreturn 0 if no error, 1 if done, -1 if error
X--------------------------------------------------------------------------*/
Xint
Xkeyset_define_key(bufptr)
Xchar *bufptr;
X{
Xregister itmp;
Xregister token_number;
Xregister KDE *tkde = (KDE *)0;
Xint KDEt = 0;
Xchar token_separator[8];
Xchar *token;
Xchar *syntax = "syntax error in key definition: %s\n";
Xchar *str_token();
X
X	if(itmp = strlen(bufptr))	/* itmp = len; if > 0 ... */
X		bufptr[--itmp] = 0;		/* ... strip trailing NL */
X	if(!itmp)
X		return(0);
X
X	if(bufptr[0] == '#')		/* comment? */
X		return(0);
X	if((*bufptr != 0x20) && (*bufptr != TAB))	/* if no leading space */
X		return(1);
X	if(*bufptr == 0)					/* if line all blank, break */
X		return(1);
X
X	while((*bufptr == 0x20) || (*bufptr == TAB))	/* strip lding sp/tab */
X		bufptr++;
X
X	token_number = 0;
X	strcpy(token_separator,":");
X	while(token = str_token(bufptr,token_separator))
X	{
X		bufptr = (char *)0;	/* further calls to str_token need NULL */
X		switch(token_number)
X		{
X			case 0:		/* first field is key identifier */
X				if((KDEt = keyset_idnum(token)) < 0)
X				{
X					pprintf(syntax,keyset_name);
X					pprintf("  %s is not a legal key identifier\n",token);
X					return(-1);
X				}
X				if(KDEt == KDEk_HOME)
X				{
X					pprintf(syntax,keyset_name);
X					pprintf("  HOME cannot be redefined!\n");
X					return(-1);
X				}
X				if(KDEt == KDEk_CU5)
X				{
X					pprintf(syntax,keyset_name);
X					pprintf("  CUR5 cannot be redefined!\n");
X					return(-1);
X				}
X				if(KDEt == KDEk_BKTAB)
X				{
X					pprintf(syntax,keyset_name);
X					pprintf("  BkTab cannot be redefined!\n");
X					return(-1);
X				}
X				tkde = &keyset_table[KDEt];
X				tkde->logical[0] = 0;
X				tkde->count = 0;
X				break;
X
X			case 1:		/* second field is logical key name */
X				strncpy(tkde->logical,token,sizeof(tkde->logical));
X				tkde->logical[sizeof(tkde->logical) - 1] = 0;
X				strcpy(token_separator," \t"); /* whitespace is tok sep now */
X				break;
X
X			default:	/* third and subsequent to define key */
X				if(!strlen(token))
X					continue;
X				if(tkde->count == sizeof(tkde->str))
X				{
X					pprintf(syntax,keyset_name);
X					pprintf("  %s: output count too long",
X						keyset_idstr(KDEt));
X					return(-1);
X				}
X				if((itmp = ascii_to_hex(token)) < 0)
X				{
X					pprintf(syntax,keyset_name);
X					pprintf("  %s: '%s' invalid code\n",
X						keyset_idstr(KDEt),token);
X					return(0);
X				}
X				tkde->str[tkde->count] = itmp;
X				tkde->count++;
X				break;
X		}	/* end of switch(token_number) */
X		token_number++;
X	}		/* end while not end of record */
X
X	return(0);
X}	/* end of keyset_define_key */
X
X/*+-------------------------------------------------------------------------
X	keyset_read(name)
Xreturns 0 on success, -1 if no .ecu/keys, -2 if no 'name', -3 if error
X--------------------------------------------------------------------------*/
Xint
Xkeyset_read(name)
Xchar *name;
X{
Xregister itmp = 0;
Xregister char *cptr;
Xstatic char ecukeys_name[128];
Xchar readkde_buf[128];
XFILE *fp_keys;
X
X	if(!ecukeys_name[0])
X	{
X		get_home_dir(ecukeys_name);
X		strcat(ecukeys_name,"/.ecu/keys");
X	}
X
X	if((fp_keys = fopen(ecukeys_name,"r")) == NULL)
X		return(-1);
X
X/* find keyset name */
X	while(fgets(readkde_buf,sizeof(readkde_buf),fp_keys) != NULL)
X	{
X		if(readkde_buf[0] == '#')		/* comment? */
X			continue;
X		if(itmp = strlen(readkde_buf))	/* itmp = len; if > 0 ... */
X			readkde_buf[--itmp] = 0;	/* ... strip trailing NL */
X		if(!itmp)
X			continue;
X		if(strcmp(readkde_buf,name) == 0)
X		{
X			itmp = 1;		/* indicate success */
X			break;
X		}
X		itmp = 0;			/* if loop terminates w/o find, failure */
X	}
X	if(!itmp)
X	{
X		fclose(fp_keys);
X		return(-2);
X	}
X
X	keyset_init();	/* clear any previous key defns */
X	strncpy(keyset_name,name,sizeof(keyset_name));
X	keyset_name[sizeof(keyset_name) - 1] = 0;
X
X/*
X * read past any other keyset names matching this set
X * process 1st line of definition when found
X */
X	while(fgets(readkde_buf,sizeof(readkde_buf),fp_keys) != NULL)
X	{
X		cptr = readkde_buf;				/* first call to str_token, -> buff */
X		if((*cptr == 0x20) || (*cptr == TAB))	/* if leading space */
X		{
X			if((itmp = keyset_define_key(readkde_buf)) < 0)
X			{
X				fclose(fp_keys);
X				keyset_name[0] = 0;
X				return(-3);
X			}
X			break;
X		}
X	}
X
X/*
X * read rest of definition
X */
X	while(fgets(readkde_buf,sizeof(readkde_buf),fp_keys) != NULL)
X	{
X		if((itmp = keyset_define_key(readkde_buf)) < 0)
X		{
X			fclose(fp_keys);
X			keyset_name[0] = 0;
X			return(-3);
X		}
X		else if(itmp)
X			break;
X	}
X
X	fclose(fp_keys);
X	return(0);
X}	/* end of keyset_read */
X
X/*+-------------------------------------------------------------------------
X	ffso(str)
X--------------------------------------------------------------------------*/
Xvoid
Xffso(str)
Xchar *str;
X{
X	tcap_stand_out();
X	fputs(str,se);
X	tcap_stand_end();
X}	/* end of ffso */
X
X/*+-------------------------------------------------------------------------
X	keyset_display()
X
X F1  xxxxx  F2   xxxxx   HOME xxxxx  PGUP xxxxx
X F3  xxxxx  F4   xxxxx   END  xxxxx  PGDN xxxxx
X F5  xxxxx  F6   xxxxx   INS  xxxxx  CUR5 xxxxx
X F7  xxxxx  F8   xxxxx   BkTab xxxxx
X F9  xxxxx  F10  xxxxx   CUR^ xxxxx  CUR> xxxxx
X F11 xxxxx  F12  xxxxx   CUR< xxxxx  CURv xxxxx
X--------------------------------------------------------------------------*/
Xvoid
Xkeyset_display()
X{
Xint itmp;
Xint itmp2;
Xint clen1 = 0;
Xchar cfmt1[32];
Xint clen2 = 0;
Xchar cfmt2[32];
Xint clen3 = 0;
Xchar cfmt3[32];
Xchar cfmt4[32];
XKDE *tkde;
X
X	if(!keyset_name[0])
X	{
X		keyset_init();
X		ff(se,"   no key definition active\r\n\r\n");
X		ff(se,"HOME - command prefix\r\n");
X		ff(se,"BkTab  - redisplay receiver screen\r\n");
X		ff(se,"cursor down - local shell in %s\r\n",curr_dir);
X		fputs("\r\n",se);
X	}
X	else
X	{
X		for(itmp = 0; itmp < KDE_COUNT; itmp++)
X		{
X			tkde = &keyset_table[itmp];
X			itmp2 = strlen(tkde->logical);
X#if defined(__GNUC__) && !defined(GCC140)
X			/*
X			 * do absolutely nothing except defeat optimization
X			 * see top of source; function is in ecuutil.c
X			 * GCC 1.40 fixed the bug
X			 */
X			defeat_optimize_to_work_around_bug(&itmp);
X#endif /* __GNUC__ */
X			switch(itmp)
X			{
X				case KDEk_F1: case KDEk_F3: case KDEk_F5: case KDEk_F7:
X				case KDEk_F9: case KDEk_F11:
X					if(clen1 < itmp2)
X						clen1 = itmp2;
X					break;
X
X				case KDEk_F2: case KDEk_F4: case KDEk_F6: case KDEk_F8:
X				case KDEk_F10: case KDEk_F12:
X					if(clen2 < itmp2)
X						clen2 = itmp2;
X					break;
X
X				case KDEk_HOME: case KDEk_END: case KDEk_INS:
X				case KDEk_CUU: case KDEk_CUL:
X					if(clen3 < itmp2)
X						clen3 = itmp2;
X					break;
X
X				case KDEk_InitStr:	/* initialization string excluded */
X					break;
X			}
X		}
X		sprintf(cfmt1," %%-%d.%ds",clen1,clen1);
X		sprintf(cfmt2," %%-%d.%ds",clen2,clen2);
X		sprintf(cfmt3," %%-%d.%ds",clen3,clen3);
X		strcpy(cfmt4," %s");
X		ff(se,"   key definition: %s\r\n\r\n",keyset_name);
X
X		ffso(" F1  ");ff(se,cfmt1,keyset_table[KDEk_F1].logical);
X		fputs("  ",se);
X		ffso(" F2  ");ff(se,cfmt2,keyset_table[KDEk_F2].logical);
X		fputs("  ",se);
X		ffso(" Home ");ff(se,cfmt3,keyset_table[KDEk_HOME].logical);
X		fputs("  ",se);
X		ffso(" PgUp ");ff(se,cfmt4,keyset_table[KDEk_PGUP].logical);
X		fputs("\r\n",se);
X
X		ffso(" F3  ");ff(se,cfmt1,keyset_table[KDEk_F3].logical);
X		fputs("  ",se);
X		ffso(" F4  ");ff(se,cfmt2,keyset_table[KDEk_F4].logical);
X		fputs("  ",se);
X		ffso(" End  ");ff(se,cfmt3,keyset_table[KDEk_END].logical);
X		fputs("  ",se);
X		ffso(" PgDn ");ff(se,cfmt4,keyset_table[KDEk_PGDN].logical);
X		fputs("\r\n",se);
X
X		ffso(" F5  ");ff(se,cfmt1,keyset_table[KDEk_F5].logical);
X		fputs("  ",se);
X		ffso(" F6  ");ff(se,cfmt2,keyset_table[KDEk_F6].logical);
X		fputs("  ",se);
X		ffso(" Ins  ");ff(se,cfmt3,keyset_table[KDEk_INS].logical);
X		fputs("  ",se);
X		ffso(" CUR5 ");ff(se,cfmt4,keyset_table[KDEk_CU5].logical);
X		fputs("\r\n",se);
X
X		ffso(" F7  ");ff(se,cfmt1,keyset_table[KDEk_F7].logical);
X		fputs("  ",se);
X		ffso(" F8  ");ff(se,cfmt2,keyset_table[KDEk_F8].logical);
X		fputs("  ",se);
X		ffso(" BkTab");ff(se,cfmt3,keyset_table[KDEk_BKTAB].logical);
X		fputs("\r\n",se);
X
X		ffso(" F9  ");ff(se,cfmt1,keyset_table[KDEk_F9].logical);
X		fputs("  ",se);
X		ffso(" F10 ");ff(se,cfmt2,keyset_table[KDEk_F10].logical);
X		fputs("  ",se);
X		ffso(" CUR^ ");ff(se,cfmt3,keyset_table[KDEk_CUU].logical);
X		fputs("  ",se);
X		ffso(" CUR> ");ff(se,cfmt4,keyset_table[KDEk_CUR].logical);
X		fputs("\r\n",se);
X
X		ffso(" F11 ");ff(se,cfmt1,keyset_table[KDEk_F11].logical);
X		fputs("  ",se);
X		ffso(" F12 ");ff(se,cfmt2,keyset_table[KDEk_F12].logical);
X		fputs("  ",se);
X		ffso(" CUR< ");ff(se,cfmt3,keyset_table[KDEk_CUL].logical);
X		fputs("  ",se);
X		ffso(" CURv ");ff(se,cfmt4,keyset_table[KDEk_CUD].logical);
X		fputs("\r\n\r\n",se);
X	}
X
X}	/* end of keyset_display */
X
X/* end of ecufkey.c */
X/* vi: set tabstop=4 shiftwidth=4: */
SHAR_EOF
echo 'File ecufkey.c is complete' &&
$TOUCH -am 0725125591 'ecufkey.c' &&
chmod 0644 ecufkey.c ||
echo 'restore of ecufkey.c failed'
Wc_c="`wc -c < 'ecufkey.c'`"
test 14071 -eq "$Wc_c" ||
	echo 'ecufkey.c: original size 14071, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= ecufkey.h ==============
if test -f 'ecufkey.h' -a X"$1" != X"-c"; then
	echo 'x - skipping ecufkey.h (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting ecufkey.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'ecufkey.h' &&
X/*+-------------------------------------------------------------------------
X	ecufkey.h -- AT XENIX/UNIX function key phrases
X	wht@n4hgf.Mt-Park.GA.US
X--------------------------------------------------------------------------*/
X/*+:EDITS:*/
X/*:07-25-1991-12:55-wht@n4hgf-ECU release 3.10 */
X/*:08-14-1990-20:40-wht@n4hgf-ecu3.00-flush old edit history */
X
X/*
X * indices to key mapping tables
X *
X * these are close to being magic numbers - don't change w/o looking
X * at KDEk_to_XF in ecutty.c
X */
X#define KDEk_F1			0
X#define KDEk_F2			1
X#define KDEk_F3			2
X#define KDEk_F4			3
X#define KDEk_F5			4
X#define KDEk_F6			5
X#define KDEk_F7			6
X#define KDEk_F8			7
X#define KDEk_F9			8
X#define KDEk_F10		9
X#define KDEk_F11		10
X#define KDEk_F12		11
X#define KDEk_CUU		12
X#define KDEk_CUD		13
X#define KDEk_CUL		14
X#define KDEk_CUR		15
X#define KDEk_CU5		16
X#define KDEk_PGUP		17
X#define KDEk_PGDN		18
X#define KDEk_END		19
X#define KDEk_INS		20
X#define KDEk_BKTAB		21
X#define KDEk_HOME		22
X#define KDEk_InitStr	23		/* initialization string kludge */
X
X#define KDE_COUNT		24
X
X#define KDE_LOGICAL_MAX		12
X#define KDE_OUTSTR_MAX		32
X
X#define KDEt_NAME		1
X#define KDEt_ENTRY		2
X#define KDEt_COMMENT	3
X#define KDEt_EOF		4
X
Xtypedef struct kde
X{
X	char logical[KDE_LOGICAL_MAX];
X	uchar KDEt;
X	char count;
X	char str[KDE_OUTSTR_MAX];
X}	KDE;
X
X/* when an count is not a count but an action: */
X#define KACT_COMMAND			-1
X#define KACT_LOCAL_SHELL		-2
X#define KACT_REDISPLAY			-3
X
Xtypedef struct XF_KDE_NAME
X{
X	uchar xf;
X	char kde;
X	char *name;
X}	XF_KDE_NAME;
X
X/* vi: set tabstop=4 shiftwidth=4: */
X/* end of ecufkey.h */
SHAR_EOF
$TOUCH -am 0725125591 'ecufkey.h' &&
chmod 0644 ecufkey.h ||
echo 'restore of ecufkey.h failed'
Wc_c="`wc -c < 'ecufkey.h'`"
test 1591 -eq "$Wc_c" ||
	echo 'ecufkey.h: original size 1591, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= ecufork.c ==============
if test -f 'ecufork.c' -a X"$1" != X"-c"; then
	echo 'x - skipping ecufork.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting ecufork.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'ecufork.c' &&
X/*+-------------------------------------------------------------------------
X	ecufork.c -- ecu spawning ground
X	wht@n4hgf.Mt-Park.GA.US
X
X  Defined functions:
X	exec_cmd(cmdstr)
X	expand_wildcard_list(wild,&expcmd)
X	find_executable(progname)
X	is_executable(progname)
X	shell(shellcmd)
X	smart_fork()
X
X--------------------------------------------------------------------------*/
X/*+:EDITS:*/
X/*:07-25-1991-12:55-wht@n4hgf-ECU release 3.10 */
X/*:07-17-1991-07:04-wht@n4hgf-avoid SCO UNIX nap bug */
X/*:09-19-1990-19:36-wht@n4hgf-ecu_log_event now gets pid for log from caller */
X/*:08-14-1990-20:40-wht@n4hgf-ecu3.00-flush old edit history */
X
X#include "ecu.h"
X#include "ecufork.h"
X
Xextern int rcvr_pid;
Xextern int last_child_wait_status;
Xextern int last_child_wait_pid;
X
X/*+-------------------------------------------------------------------------
X	smart_fork()
X--------------------------------------------------------------------------*/
Xint
Xsmart_fork()
X{
Xregister int count = 5;
Xregister int pid;
X
X	while(count--)
X	{
X		if((pid = fork()) >= 0)
X			return(pid);
X		if(count)
X			nap(40L);
X	}
X	return(-1);
X}	/* end of smart_fork */
X
X/*+-----------------------------------------------------------------------
X	shell(shellcmd)
X
X  param 'shellcmd' is a shell command prefixed with either
X  a '!', '$', '>' character.
X
X  '!' causes the command to run as a normal subshell of a process.
X  '$' causes the communications line to be stdin and stdout
X      for the spawned shell
X  '>' causes spawned shell to receive exactly sames files as ecu
X------------------------------------------------------------------------*/
Xvoid
Xshell(shellcmd)
Xchar *shellcmd;
X{
Xregister shellpid;
Xregister itmp;
Xregister char *cptr;
X#if defined(FORK_DEBUG)
Xchar s40[40];
X#endif
Xint wait_status;
X
Xint rcvr_alive = (rcvr_pid > 0);
Xchar *getenv();
X
X	if(rcvr_alive)
X		kill_rcvr_process(SIGUSR1);		/* stop receiver process gracefully */
X
X	signal(SIGINT,SIG_IGN);
X	signal(SIGTERM,SIG_IGN);
X	signal(SIGUSR1,SIG_IGN);
X	signal(SIGUSR2,SIG_IGN);
X	signal(SIGCLD,SIG_DFL);
X
X	if((shellpid = smart_fork()) < 0)
X	{
X		ff(se,"Cannot fork\r\n");
X		if(rcvr_alive)
X			start_rcvr_process(1);
X		xmtr_signals();
X		return;
X	}
X
X	ttymode(0);  	     		/* set canonical tty mode */
X	if(shellpid == 0)			/* we are the spawned (going to call shell) */
X	{
X		if(*shellcmd != '>')	/* '>' prefix means leave fd's alone! */
X		{
X			/* Hook-up our "standard output" to either the tty or
X			 * the line as appropriate for '!' or '$' */
X			close(TTYOUT);
X			fcntl(((*shellcmd == '$') ? shm->Liofd : TTYERR),F_DUPFD,TTYOUT);
X			if(*shellcmd == '$')
X			{
X				close(TTYIN);
X				fcntl(shm->Liofd,F_DUPFD,TTYIN);
X			}
X			close(shm->Liofd);
X		}
X
X		child_signals();		/* signals for child */
X
X		if(*shellcmd == '!')
X		{
X			cptr = getenv("SHELL");
X			if(cptr == (char *)0)
X				cptr = "/bin/csh";
X		}
X		else
X			cptr = "/bin/sh";
X
X		shellcmd++;
X		child_signals();
X		if(ulindex(cptr,"csh") > -1)
X		{
X			if(*shellcmd == '\0')
X				execl(cptr,"csh",(char *)0);
X			else
X				execl(cptr,"csh","-c",shellcmd,(char *)0);
X		}
X		else
X		{
X			if(*shellcmd == '\0')
X				execl(cptr,"sh",(char *)0);
X			else
X				execl(cptr,"sh","-c",shellcmd,(char *)0);
X		}
X
X		ff(se,"cannot execute %s\r\n",cptr);	/* should not get here */
X		_exit(255);								/* end of spawned process */
X	}	/* end of if child process */
X
X#if defined(FORK_DEBUG)
X	sprintf(s40,"DEBUG fork shell pid %d",shellpid);
X	ecu_log_event(getpid(),s40); /* shell */
X#endif
X
X	while(((itmp = wait(&wait_status)) != shellpid) && (itmp != -1))
X		;
X	last_child_wait_status = wait_status;
X	last_child_wait_pid = shellpid;
X
X	xmtr_signals();			/* restore standard xmtr signals */
X	ttymode(1);				/* control tty back to raw mode */
X
X/* any comm program will probably doodle with the line characteristics. */
X/* we want to make sure they are restored to normal */
X	lreset_ksr();			/* restore comm line params */
X
X	if(rcvr_alive)
X		start_rcvr_process(1);
X
X}	/* end of shell */
X
X/*+-------------------------------------------------------------------------
X	is_executable(progname)
X--------------------------------------------------------------------------*/
Xis_executable(progname)
Xchar *progname;
X{
Xstruct stat ss;
X
X	if(stat(progname,&ss) < 0)			/* if cannot stat, flunk */
X		return(0);
X	if((ss.st_mode & 0111) == 0)		/* if no --x--x--x, flunk */
X		return(0);
X	return(1);	/* whew, this OUGHT to work */
X
X}	/* end of is_executable */
X
X/*+-------------------------------------------------------------------------
X	find_executable(progname)
XPATH=':/usr/wht/bin:/bin:/usr/bin:/usr/wht/bin:/etc/tuckerware' len=56
X--------------------------------------------------------------------------*/
Xchar *
Xfind_executable(progname)
Xchar *progname;
X{
Xregister itmp;
Xstatic char *path_buf = (char *)0;
X#define PATHNAME_QUAN 32
Xstatic char *path_name[PATHNAME_QUAN + 1];
Xstatic char rtn_path[256];
Xstatic int path_count = 0;
Xchar *cptr;
Xchar *getenv();
X
X	if(path_buf == (char *)0)
X	{
X		if((cptr = getenv("PATH")) == (char *)0)
X			return(cptr);
X		if(!(path_buf = malloc(strlen(cptr) + 1)))
X			return((char *)0);
X		strcpy(path_buf,cptr);
X		path_name[PATHNAME_QUAN + 1] = (char *)0;
X		cptr = path_buf;
X		for(path_count = 0; path_count < PATHNAME_QUAN; path_count++)
X		{
X			if(*cptr == 0)
X				break;
X			path_name[path_count] = cptr;
X			while((*cptr != ':') && (*cptr != 0))
X				cptr++;
X			if(*cptr == ':')
X				*cptr++ = 0;
X		}
X	}	/* end of get and process path env variable */
X
X/* look for executable */
X	for(itmp = 0; itmp < path_count; itmp++)
X	{
X		if(*path_name[itmp] == 0)	/* if null path (./) */
X			strcpy(rtn_path,"./");
X		else
X			sprintf(rtn_path,"%s/",path_name[itmp]);
X		strcat(rtn_path,progname);
X		if(is_executable(rtn_path))
X			return(rtn_path);
X	}
X	return((char *)0);
X}	/* end of find_executable */
X
X/*+-------------------------------------------------------------------------
X	exec_cmd(cmdstr) - execute an arbitrary program with arguments
Xkills rcvr process if alive and restarts it when done if was alive
X--------------------------------------------------------------------------*/
Xexec_cmd(cmdstr)
Xchar *cmdstr;
X{
Xchar *cmdpath;
X#define MAX_EXEC_ARG 512
Xchar *cmdargv[MAX_EXEC_ARG];
Xint itmp;
Xint execpid;
Xint rcvr_alive = (rcvr_pid > 0);
Xint old_ttymode = get_ttymode();
Xint wait_status = 0;
Xchar *strrchr();
X
X#if defined(FORK_DEBUG)
Xchar s80[80];
X	strcpy(s80,"DEBUG exec ");
X	strncat(s80,cmdstr,sizeof(s80)-12);
X	s80[sizeof(s80)-12] = 0;
X	ecu_log_event(getpid(),s80);
X#endif
X
X	build_arg_array(cmdstr,cmdargv,MAX_EXEC_ARG,&itmp);
X	if(itmp == MAX_EXEC_ARG)
X	{
X		ff(se,"Too many arguments to command\r\n");
X		return(-1);
X	}
X	else if(!itmp)
X	{
X		ff(se,"null command\r\n");
X		return(-1);
X	}
X
X	if(*cmdargv[0] == '/')
X	{
X		cmdpath = cmdargv[0];
X		cmdargv[0] = strrchr(cmdargv[0],'/') + 1;
X	}
X	else
X	{
X		if((cmdpath = find_executable(cmdargv[0])) == (char *)0)
X		{
X			ff(se,"Cannot find %s\r\n",cmdargv[0]);
X			return(-1);
X		}
X	}
X
X	if(rcvr_alive)
X		kill_rcvr_process(SIGUSR1);		/* stop receiver process gracefully */
X
X/* this code executed by the father (forking) process */
X/* wait on death of child (morbid in life, but ok here) */
X
X	signal(SIGINT,SIG_IGN);
X	signal(SIGTERM,SIG_IGN);
X	signal(SIGUSR1,SIG_IGN);
X	signal(SIGUSR2,SIG_IGN);
X	signal(SIGCLD,SIG_DFL);
X
X	if((execpid = smart_fork()) < 0)
X	{
X		ff(se,"Cannot fork\r\n");
X		if(rcvr_alive)
X			start_rcvr_process(1);
X		xmtr_signals();
X		return(-1);
X	}
X
X	if(execpid == 0)			/* we are the spawned (going to call exec) */
X	{
X		ttymode(0);  	     	/* set canonical tty mode */
X		child_signals();
X		execv(cmdpath,cmdargv);
X		perror(cmdpath);
X		exit(255);				/* end of spawned process */
X	}	/* end of if child process */
X
X	wait_status = 0;
X	while(((itmp = wait(&wait_status)) != execpid) && (itmp != -1))
X		;
X	last_child_wait_status = wait_status;
X	last_child_wait_pid = execpid;
X
X/* resume our normally scheduled program */
X	lreset_ksr();				/* restore comm line params */
X	ttymode(old_ttymode);		/* control tty back to original */
X	if(rcvr_alive)
X		start_rcvr_process(1);
X	xmtr_signals();
X	return(last_child_wait_status);
X
X}	/* end of exec_cmd */
X
X/*+-------------------------------------------------------------------------
X	expand_wildcard_list(wild,&expcmd)
X
Xcalled with 'foo <wildcardlist>' for command expansion prior to exec()
X         or '<wildcardlist>' to expand a list of files.
X
XIf called with 'foo'-style wild, anything you want to protect from csh 
Xglobbing or other interpretation must be properly protected (quoted) --
XAND quoting will be removed one level by the csh.
X
Xif return 0, wild has been expanded, expcmd must be free()'d when done
Xif return -1, error, expcmd has error message (static message: DO NOT FREE)
X--------------------------------------------------------------------------*/
Xexpand_wildcard_list(wild,expcmd)
Xchar *wild;
Xchar **expcmd;
X{
Xregister char *cptr;
X#define P_READ 0
X#define P_WRITE 1
Xint pipe_pid;
Xint stdout_pipe[2];
Xint stderr_pipe[2];
Xint count;
Xint expcmd_size = 0;
Xint itmp;
Xint wait_status;
Xint rcvr_alive = (rcvr_pid > 0);
XFILE *fp_pipe = (FILE *)0;
Xchar *echo_cmd;
Xstatic char s132[132];
Xextern int errno;
Xextern char *sys_errlist[];
Xchar *strchr();
Xstatic char *pipe_err_msg = "system error: no pipe";
Xstatic char *mem_err_msg = "system error: no memory";
X
X	if(strchr(wild,'<') || strchr(wild,'>') || strchr(wild,'&'))
X	{
X		*expcmd = "illegal characters: '<', '>' or '&'";
X		return(-1);
X	}
X
X	if(pipe(stdout_pipe) < 0)
X	{
X		*expcmd = pipe_err_msg;
X		return(-1);
X	}
X	if(pipe(stderr_pipe) < 0)
X	{
X		close(stdout_pipe[P_READ]);
X		close(stdout_pipe[P_WRITE]);
X		*expcmd = pipe_err_msg;
X		return(-1);
X	}
X	if(!(echo_cmd = malloc(strlen(wild) + 10)))
X	{
X		close(stdout_pipe[P_READ]);
X		close(stdout_pipe[P_WRITE]);
X		close(stderr_pipe[P_READ]);
X		close(stderr_pipe[P_WRITE]);
X		*expcmd = mem_err_msg;
X		return(-1);
X	}
X
X	strcpy(echo_cmd,"echo ");
X	strcat(echo_cmd,wild);
X
X	if(rcvr_alive)
X		kill_rcvr_process(SIGUSR1);		/* stop receiver process gracefully */
X
X	signal(SIGINT,SIG_IGN);
X	signal(SIGTERM,SIG_IGN);
X	signal(SIGUSR1,SIG_IGN);
X	signal(SIGUSR2,SIG_IGN);
X	signal(SIGCLD,SIG_DFL);
X
X	if((pipe_pid = smart_fork()) == 0)
X	{
X	int null = open("/dev/null",O_WRONLY,0);
X
X		close(stdout_pipe[P_READ]);
X		close(TTYOUT);
X		dup(stdout_pipe[P_WRITE]);
X		close(stdout_pipe[P_WRITE]);
X		close(TTYERR);
X		dup(stderr_pipe[P_WRITE]);
X		close(stderr_pipe[P_WRITE]);
X		close(null);
X		child_signals();
X		execl("/bin/csh","csh","-e","-f","-c",echo_cmd,(char *)0);
X		_exit(255);
X	}
X
X#if defined(FORK_DEBUG)
X	sprintf(s132,"DEBUG expand pid %d",pipe_pid);
X	ecu_log_event(getpid(),s132);		/* expand_wildcard_list */
X#endif
X
X	free(echo_cmd);
X
X	close(stdout_pipe[P_WRITE]);
X	close(stderr_pipe[P_WRITE]);
X	if(pipe_pid == -1)
X	{
X		close(stdout_pipe[P_READ]);
X		close(stderr_pipe[P_READ]);
X		*expcmd = "could not fork";
X		if(rcvr_alive)
X			start_rcvr_process(0);
X		xmtr_signals();
X		return(-1);
X	}
X
X	if(!(*expcmd = malloc(expcmd_size = 5120)))
X	{
X		close(stdout_pipe[P_READ]);
X		close(stderr_pipe[P_READ]);
X		kill(pipe_pid,SIGKILL);
X		*expcmd = mem_err_msg;
X		if(rcvr_alive)
X			start_rcvr_process(0);
X		xmtr_signals();
X		return(-1);
X	}
X
X	if(!(fp_pipe = fdopen(stdout_pipe[P_READ],"r")) ||
X		((count = fread(*expcmd,1,expcmd_size,fp_pipe)) < 0))
X	{
X			free(*expcmd);
X			kill(pipe_pid,SIGKILL);
X			close(stdout_pipe[P_READ]);
X			close(stderr_pipe[P_READ]);
X			*expcmd = "error reading wild list expansion";
X			if(rcvr_alive)
X				start_rcvr_process(0);
X			xmtr_signals();
X			return(-1);
X	}
X
X	/*
X	 * make sure stdout is closed
X	 */
X	if(fp_pipe)
X		fclose(fp_pipe);
X	close(stdout_pipe[P_READ]);
X
X	/*
X	 * if no expansion, read stderr to find out why
X	 */
X	if(!count)
X	{
X		free(*expcmd);
X		count = read(stderr_pipe[P_READ],s132,sizeof(s132) - 1);
X		if(count < 0)
X			strcpy(s132,sys_errlist[errno]);
X		else
X			s132[count] = 0;
X		if(s132[count - 1] == '\n')
X			s132[count - 1] = 0;
X		close(stderr_pipe[P_READ]);
X		if(strncmp(s132,"echo: ",6))
X			*expcmd = s132;
X		else
X			*expcmd = s132 + 6;
X		if(rcvr_alive)
X			start_rcvr_process(0);
X		return(-1);
X	}
X
X	/*
X	 * place trailing null
X	 * kill trailing new line
X	 */
X	if(count)
X	{
X		cptr = (*expcmd) + count;
X		*cptr-- = 0;
X		if(*cptr == '\n')
X			*cptr = 0;
X	}
X
X	/*
X	 * clean up zombie
X	 */
X	wait_status = 0;
X	while(((itmp = wait(&wait_status)) != pipe_pid) && (itmp != -1))
X		;
X
X	xmtr_signals();
X
X	/*
X	 * if bad termination status, read stderr
X	 */
X	if(wait_status)
X	{
X		free(*expcmd);
X		count = read(stderr_pipe[P_READ],s132,sizeof(s132) - 1);
X		if(count < 0)
X			strcpy(s132,sys_errlist[errno]);
X		else
X			s132[count] = 0;
X		if(s132[count - 1] == '\n')
X			s132[count - 1] = 0;
X		close(stderr_pipe[P_READ]);
X		if(strncmp(s132,"echo: ",6))
X			*expcmd = s132;
X		else
X			*expcmd = s132 + 6;
X		if(rcvr_alive)
X			start_rcvr_process(0);
X		return(-1);
X	}
X	close(stderr_pipe[P_READ]);
X
X	/*
X	 * whew: we have (I think) a file list expansion
X	 */
X	if(rcvr_alive)
X		start_rcvr_process(0);
X
X	return(0);
X}	/* end of expand_wildcard_list */
X/* vi: set tabstop=4 shiftwidth=4: */
SHAR_EOF
$TOUCH -am 0725125591 'ecufork.c' &&
chmod 0644 ecufork.c ||
echo 'restore of ecufork.c failed'
Wc_c="`wc -c < 'ecufork.c'`"
test 12907 -eq "$Wc_c" ||
	echo 'ecufork.c: original size 12907, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= ecufork.h ==============
if test -f 'ecufork.h' -a X"$1" != X"-c"; then
	echo 'x - skipping ecufork.h (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting ecufork.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'ecufork.h' &&
X/*+-------------------------------------------------------------------------
X	ecufork.h 
X	wht@n4hgf.Mt-Park.GA.US
X
Xfor now just vehicle for deciding whether or not to debug fork/waits
X--------------------------------------------------------------------------*/
X/*+:EDITS:*/
X/*:07-25-1991-12:55-wht@n4hgf-ECU release 3.10 */
X/*:08-14-1990-20:40-wht@n4hgf-ecu3.00-flush old edit history */
X
X/* #define FORK_DEBUG */
X/* vi: set tabstop=4 shiftwidth=4: */
SHAR_EOF
$TOUCH -am 0725125591 'ecufork.h' &&
chmod 0644 ecufork.h ||
echo 'restore of ecufork.h failed'
Wc_c="`wc -c < 'ecufork.h'`"
test 452 -eq "$Wc_c" ||
	echo 'ecufork.h: original size 452, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= ecuicmaux.c ==============
if test -f 'ecuicmaux.c' -a X"$1" != X"-c"; then
	echo 'x - skipping ecuicmaux.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting ecuicmaux.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'ecuicmaux.c' &&
X/*+-------------------------------------------------------------------------
X	ecuicmaux.c -- ecuicmd.c auxiliary functions
X	wht@n4hgf.Mt-Park.GA.US
X
X  Defined functions:
X	display_ascii_names()
X	nlin_nlout_control(token,narg,arg)
X	icmd_conversion(token,narg,arg)
X	icmd_log(token,narg,arg)
X
X--------------------------------------------------------------------------*/
X/*+:EDITS:*/
X/*:07-25-1991-12:56-wht@n4hgf-ECU release 3.10 */
X/*:07-04-1991-20:03-wht@n4hgf-make pcmd_log */
X/*:08-14-1990-20:40-wht@n4hgf-ecu3.00-flush old edit history */
X
X#include "ecu.h"
X#include "ecuerror.h"
X#include "ecucmd.h"
X#include "esd.h"
X
Xextern int rcvr_pid;
X
Xint rcvr_log = 0;				/* if non-zero, logging rcvd data */
Xchar rcvr_log_file[256];		/* log filename */
Xint rcvr_log_gen_title = 0;		/* gen log header on next open (rcvr process) */
XFILE *rcvr_log_fp = (FILE *)0;	/* rcvr log file */
Xint rcvr_log_append = 1;
Xint rcvr_log_raw = 0;
Xint rcvr_log_flusheach = 0;
X
X/*+-----------------------------------------------------------------------
X	display_ascii_names()
X------------------------------------------------------------------------*/
Xvoid
Xdisplay_ascii_names()
X{
Xregister intval;
X
X	for(intval = 0; intval < 32; intval++)
X	{
X		ff(se,"%s %3d %03o %02x ^%c | ",hex_to_ascii_name((intval)),
X				(intval),(intval),(intval),(intval) |0x40);
X		ff(se,"    %3d %03o %02x  %c | ",
X				intval+32,intval+32,intval+32,intval+32);
X		ff(se,"    %3d %03o %02x  %c | ",
X				intval+64,intval+64,intval+64,intval+64);
X		if(intval != 31)
X		{
X			ff(se,"    %3d %03o %02x  %c\r\n",
X					intval+96,intval+96,intval+96,intval+96);
X		}
X		else
X		{
X			ff(se,"    %3d %03o %02x  ^?\r\n",
X					intval+96,intval+96,intval+96);
X		}
X	}
X
X}	/* end of display_ascii_names */
X
X/*+-------------------------------------------------------------------------
X	icmd_conversion(token,narg,arg)
X--------------------------------------------------------------------------*/
Xvoid
Xicmd_conversion(token,narg,arg)
Xint token;
Xint narg;
Xchar **arg;
X{
Xregister itmp;
X
X	switch(token)
X	{
X	case CTxa:
X	case CToa:
X	case CTda:
X		if(narg > 1)
X		{
X		int result;
X		char format[4];
X			sprintf(format,"%%%c",to_lower(*arg[0]));
X			if(sscanf(arg[1],format,&result) == 0)
X			{
X				ff(se,"  invalid argument\r\n");
X				return;
X			}
X			result &= 0xFF;
X			if(result == ' ')
X				ff(se," == ' ' 0x20\r\n",make_char_graphic(result,1));
X			else
X				ff(se," == %s\r\n",make_char_graphic(result,1));
X		}
X		else
X		{
X			ff(se,"\r\n");
X			display_ascii_names();
X		}
X		break;
X	case CTax:
X		if(arg[1] == (char *)0)
X		{
X			ff(se,"\r\n");
X			display_ascii_names();
X			break;
X		}
X		switch(strlen(arg[1]))
X		{
X			case 1:
X				ff(se," == 0x%02x\r\n",*arg[1]);
X				break;
X			case 2:
X				if(*arg[1] == '^')
X				{
X					itmp = to_upper(*(arg[1] + 1));
X					if((itmp < '@') || (itmp > '_'))
X					{
X						ff(se,"  not a valid control character\r\n");
X						return;
X					}
X					itmp &= 0x1F;
X					ff(se," == 0x%02x %s\r\n",itmp,make_char_graphic(itmp,1));
X					break;
X				}	/* else fall thru */
X			case 3:
X				if((itmp = ascii_name_to_hex(arg[1])) > -1)
X				{
X					ff(se," == 0x%02x %s\r\n",itmp,make_char_graphic(itmp,1));
X					break;
X				}	/* else fall thru */
X			default:
X				ff(se,"  invalid ... examples of valid parameters:\r\n");
X				ff(se,"        ^A ETX  or  printable character\r\n");
X				break;
X		}
X		break;
X	default:
X		ff(se,"  invalid command\r\n");
X	}
X}	/* end of icmd_conversion */
X
X/*+-------------------------------------------------------------------------
X	rcvr_log_control(token,narg,arg)
X--------------------------------------------------------------------------*/
Xint
Xrcvr_log_control(token,narg,arg)
Xint token;
Xint narg;
Xchar **arg;
X{
Xregister itmp;
Xregister itmp2;
Xextern int proc_level;
Xextern int proctrace;
X#if defined(M_XENIX) || defined(M_UNIX)
Xchar *lparg = "/dev/lp1";
X#endif
X
X	switch(token)
X	{
X	case CTloff:
X		goto LOG_OFF;
X		break;
X
X#if defined(M_XENIX) || defined(M_UNIX)
X	case CTllp:
X		narg = 1;
X		arg = &lparg;
X		/* fall thru */
X#endif
X
X	case CTlog:
X		if(narg > 1)
X		{
X			if(minunique("off",arg[1],3))
X			{
XLOG_OFF:
X				shmx_set_rcvr_log("",0,0,0);
X				rcvr_log = 0;
X				rcvr_log_file[0] = 0;
X				return(0);
X			}
X/* turning logging on */
X			itmp2 = -1;
X			rcvr_log_append = 1;
X			rcvr_log_raw = 0;
X			for(itmp = 1; itmp < narg; itmp++)
X			{
X				if(*arg[itmp] == '-')
X				{
X					switch(arg[itmp][1])
X					{
X						case 's': rcvr_log_append = 0; break;
X						case 'r': rcvr_log_raw = 1; break;
X						case 'f': rcvr_log_flusheach = 1; break;
X						default:
X							pprintf("unrecognized switch -%c\n",arg[itmp][1]);
X							log_cmd_usage();
X						return(eFATAL_ALREADY);
X					}
X				}
X				else
X				{
X					if(itmp2 > 0)
X					{
X						pputs("too many arguments\n");
X						log_cmd_usage();
X						return(eFATAL_ALREADY);
X					}
X					itmp2 = itmp;
X				}
X			}
X			if(itmp2 < 0)
X			{
X				pputs("no log file name specified\n");
X				log_cmd_usage();
X				return(eFATAL_ALREADY);
X			}
X			if(arg[itmp2][0] != '/')	/* if log file not full path, ... */
X			{							/* ... supply current directory */
X				get_curr_dir(rcvr_log_file,
X					sizeof(rcvr_log_file) - strlen(arg[itmp2]) - 2);
X				strcat(rcvr_log_file,"/");
X				strcat(rcvr_log_file,arg[itmp2]);
X			}
X			else
X				strcpy(rcvr_log_file,arg[itmp2]);
X
X			/* try to open the file if we can */
X			rcvr_log_fp = fopen(rcvr_log_file,"a");
X			if(rcvr_log_fp)		/* if success */
X			{
X				fclose(rcvr_log_fp);
X				rcvr_log_fp = NULL;
X				rcvr_log = 1;
X				shmx_set_rcvr_log(rcvr_log_file,rcvr_log_append,rcvr_log_raw,
X					rcvr_log_flusheach);
X			}
X			else		/* xmtr() could not open file */
X			{
X				pputs("could not open ");
X				pperror(rcvr_log_file);
X				return(eFATAL_ALREADY);
X			}
X			rcvr_log_append = 1;
X		}	/* end of if argument to command */
X
X		if(rcvr_log && (!proc_level || proctrace))
X		{
X			pprintf("\n%sing received %s text to\n%s\n",
X				(rcvr_log_append) ? "append" : "writ",
X				(rcvr_log_raw)    ? "raw"    : "filtered",
X				rcvr_log_file);
X		}
X		else if (!proc_level || proctrace)
X			pputs("not logging\n");
X		break;
X
X	default:
X		pputs("invalid command\n");
X		return(eFATAL_ALREADY);
X	}
X	return(0);
X}	/* end of rcvr_log_control */
X
X/*+-------------------------------------------------------------------------
X	pcmd_rlog(param) - control receivert logging (script)
X
Xrlog [-srf] ['filename']  #  see ecuidmc.d
Xrlog 'off'
X
XThis is a hack to use icmd stuff from proc language
X--------------------------------------------------------------------------*/
Xint
Xpcmd_rlog(param)
XESD *param;
X{
Xint erc;
Xint lnarg = 0;
Xchar *larg[3];
XESD *tesd = esdalloc(256);
Xchar switches[8];
X
X	if(!tesd)
X		return(eNoMemory);
X
X	larg[lnarg++] = "log";
X
X	if(!get_switches(param,switches,sizeof(switches)))
X		larg[lnarg++] = switches;
X
X	if(!gstr(param,tesd,1))
X		larg[lnarg++] = tesd->pb;
X
X	erc = rcvr_log_control(CTlog,lnarg,larg);
X	esdfree(tesd);
X	return(erc);
X
X}	/* end of pcmd_rlog */
X
X/*+-------------------------------------------------------------------------
X	icmd_log(token,narg,arg)
X--------------------------------------------------------------------------*/
Xint
Xicmd_log(token,narg,arg)
Xint token;
Xint narg;
Xchar **arg;
X{
Xregister itmp;
Xregister itmp2;
X
X	switch(token)
X	{
X	case CTloff:
X		goto LOG_OFF;
X		break;
X#if defined(M_XENIX) || defined(M_UNIX)
X	case CTllp:
X		icmd("log /dev/lp1");
X#endif
X		break;
X	case CTlog:
X		if(narg > 1)
X		{
X			if(minunique("off",arg[1],3))
X			{
XLOG_OFF:
X				if(rcvr_log == 0)	/* "off",but not logging */
X					goto RECORD_REPORT;
X				ff(se,"\r\nlogging concluded (file %s)\r\n",rcvr_log_file);
X				shmx_set_rcvr_log("",0,0,0);
X				rcvr_log = 0;
X				rcvr_log_file[0] = 0;
X				return(0);
X			}
X/* turning logging on */
X			itmp2 = -1;
X			rcvr_log_append = 1;
X			rcvr_log_raw = 0;
X			for(itmp = 1; itmp < narg; itmp++)
X			{
X				if(*arg[itmp] == '-')
X				{
X					switch(arg[itmp][1])
X					{
X						case 's': rcvr_log_append = 0; break;
X						case 'r': rcvr_log_raw = 1; break;
X						case 'f': rcvr_log_flusheach = 1; break;
X						default:
X							ff(se,"   unrecognized switch -%c\r\n",
X										arg[itmp][1]);
X							log_cmd_usage();
X							return(eFATAL_ALREADY);
X					}
X				}
X				else
X				{
X					if(itmp2 > 0)
X					{
X						ff(se,"   too many positional arguments\r\n");
X						log_cmd_usage();
X						return(eFATAL_ALREADY);
X					}
X					itmp2 = itmp;
X				}
X			}
X			if(itmp2 < 0)
X			{
X				ff(se,"   no log file name specified\r\n");
X				log_cmd_usage();
X				return(eFATAL_ALREADY);
X			}
X			if(arg[itmp2][0] != '/')	/* if log file not full path, ... */
X			{							/* ... supply current directory */
X				get_curr_dir(rcvr_log_file,
X					sizeof(rcvr_log_file) - strlen(arg[itmp2]) - 2);
X				strcat(rcvr_log_file,"/");
X				strcat(rcvr_log_file,arg[itmp2]);
X			}
X			else
X				strcpy(rcvr_log_file,arg[itmp2]);
X
X			/* try to open the file if we can */
X			rcvr_log_fp = fopen(rcvr_log_file,"a");
X			if(rcvr_log_fp != NULL)		/* if success */
X			{
X				fclose(rcvr_log_fp);
X				rcvr_log_fp = NULL;
X				rcvr_log = 1;
X				shmx_set_rcvr_log(rcvr_log_file,rcvr_log_append,rcvr_log_raw,
X					rcvr_log_flusheach);
X			}
X			else		/* xmtr() could not open file */
X			{
X				ff(se,"   could not open ");
X				perror(rcvr_log_file);
X				ff(se,"\r\n");
X				return(eFATAL_ALREADY);
X			}
X			rcvr_log_append = 1;
X		}	/* end of if argument to command */
X
XRECORD_REPORT:
X		if(rcvr_log)
X		{
X			ff(se,"\r\n%sing received %s text to\r\n%s\r\n",
X				(rcvr_log_append) ? "append" : "writ",
X				(rcvr_log_raw)    ? "raw"    : "filtered",
X				rcvr_log_file);
X			ff(se,"use \"HOME log off\" to stop logging\r\n");
X		}
X		else
X		{
X			ff(se,"   not logging.\r\n");
X			ff(se,"use \"HOME slog <filename>\" to start logging\r\n");
X		}
X		break;
X	default:
X		ff(se,"  invalid command\r\n");
X		return(eFATAL_ALREADY);
X	}
X	return(0);
X}	/* end of icmd_log */
X
X/*+-------------------------------------------------------------------------
X	nlin_nlout_control(token,narg,arg)
X--------------------------------------------------------------------------*/
Xvoid
Xnlin_nlout_control(token,narg,arg)
Xint token;
Xint narg;
Xchar **arg;
X{
X	switch(token)
X	{
X	case CTnlin:
X		if(narg != 1)
X			shm->Ladd_nl_incoming = yes_or_no(arg[1]);
X		ff(se,"  %sappending NL to incoming CR\r\n",
X			(shm->Ladd_nl_incoming) ? "" : "not ");
X		break;
X	case CTnlout:
X		if(narg != 1)
X			shm->Ladd_nl_outgoing = yes_or_no(arg[1]);
X		ff(se,"  %sappending NL to outgoing CR\r\n",
X			(shm->Ladd_nl_outgoing) ? "" : "not ");
X		break;
X	default:
X	case CTnl:
X		ff(se,"  incoming: %s  outgoing: %s\r\n",
X			(shm->Ladd_nl_incoming) ? "CR/LF" : "CR",
X			(shm->Ladd_nl_outgoing) ? "CR/LF" : "CR");
X		break;
X	}
X
X}	/* end of nlin_nlout_control */
X
X/* end of ecuicmaux.c */
X/* vi: set tabstop=4 shiftwidth=4: */
SHAR_EOF
$TOUCH -am 0725125691 'ecuicmaux.c' &&
chmod 0644 ecuicmaux.c ||
echo 'restore of ecuicmaux.c failed'
Wc_c="`wc -c < 'ecuicmaux.c'`"
test 10590 -eq "$Wc_c" ||
	echo 'ecuicmaux.c: original size 10590, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= ecuicmd.c ==============
if test -f 'ecuicmd.c' -a X"$1" != X"-c"; then
	echo 'x - skipping ecuicmd.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting ecuicmd.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'ecuicmd.c' &&
X/*+-----------------------------------------------------------------------
X	ecuicmd.c - ECU interactive command handler
X	wht@n4hgf.Mt-Park.GA.US
X
X  Defined functions:
X	icmd(icmd_cmd)
X	icmd_do_proc(ndoarg,doarg)
X	search_cmd_list(cmd_list,cmd)
X
X------------------------------------------------------------------------*/
X/*+:EDITS:*/
X/*:07-25-1991-12:56-wht@n4hgf-ECU release 3.10 */
X/*:05-21-1991-18:22-wht@n4hgf-add pushd/popd commands */
X/*:05-21-1991-00:46-wht@n4hgf-added -3 error code to keyset_read */
X/*:03-20-1991-05:25-root@n4hgf-experimental eto command */
X/*:03-20-1991-04:55-root@n4hgf-Metro Link support + stat cmd changes */
X/*:02-04-1991-19:03-wht@n4hgf-add multiscreen tag to stat */
X/*:01-09-1991-22:31-wht@n4hgf-ISC port */
X/*:12-24-1990-04:31-wht@n4hgf-experimental fasi driver command */
X/*:08-14-1990-20:40-wht@n4hgf-ecu3.00-flush old edit history */
X
X#include "ecu.h"
X#include "esd.h"
X#include "ecufkey.h"
X#include "patchlevel.h"
X
X#define NEED_P_CMD
X#include "ecucmd.h"
X
X#if defined(FASI)
X# include <local/fas.h>
X#endif
X
Xextern KDE keyset_table[];
Xextern char *makedate;
Xextern char curr_dir[CURR_DIRSIZ];
Xextern char hello_str[];
Xextern char keyset_name[];
Xextern char rcvr_log_file[];	/* if rcvr_log!= 0,log filename */
Xextern int sigint;
Xextern int current_ttymode;
Xextern long tty_escape_timeout;		/* timeout on waiting for char after ESC */
Xextern int tcap_COLS;
Xextern int tcap_LINES;
Xextern int tty_is_multiscreen;
Xextern int tty_is_ansi;
Xextern int tty_is_pty;
X#if defined(METRLINK_X11R4_PTS)
Xextern int tty_is_pts;
X#endif /* METRLINK_X11R4_PTS */
Xextern int errno;
Xextern int proc_level;
Xextern int proctrace;
Xextern int rcvr_log;			/* rcvr log active if != 0 */
Xextern int rcvr_log_append;	/* if true, append, else scratch */
Xextern int rcvr_log_raw;		/* if true, log all, else filter ctl chrs */
Xextern int rcvr_pid;
Xextern ulong colors_current;
Xextern FILE *plog_fp;
Xextern ESD  *plog_name;
Xextern char screen_dump_file_name[];
X
Xint protocol_log_packets = 0;
Xint want_bell_notify = 1;	/* want bell notify */
X
X/*+-------------------------------------------------------------------------
X	search_cmd_list(cmd_list,cmd)
Xreturns -1 if cmd not found or insufficient chars for match
Xelse token value for command
Xproc-only commands have 'mincnt' values 0
X--------------------------------------------------------------------------*/
Xsearch_cmd_list(cmd_list,cmd)
Xregister P_CMD *cmd_list;
Xregister char *cmd;
X{
X	while(cmd_list->token != -1)
X	{
X		if(cmd_list->mincnt && minunique(cmd_list->cmd,cmd,cmd_list->mincnt))
X			return(cmd_list->token);
X		cmd_list++;
X	}
X	return(-1);
X
X}	/* end of search_cmd_list */
X
X/*+-------------------------------------------------------------------------
X	icmd_do_proc(ndoarg,doarg)
X--------------------------------------------------------------------------*/
Xicmd_do_proc(ndoarg,doarg)
Xint ndoarg;
Xchar **doarg;
X{
Xregister erc;
Xulong colors_at_entry = colors_current;
X
X	kill_rcvr_process(SIGUSR1);
X	ttymode(2);
X	erc = do_proc(ndoarg,doarg);
X	proc_file_reset();
X	ttymode(1);
X	sigint = 0;
X	setcolor(colors_notify);
X	ff(se,"[procedure finished]");
X	setcolor(colors_at_entry);
X	ff(se,"\r\n");
X	start_rcvr_process(0);
X	return(erc);
X}	/* end of icmd_do_proc */
X
X/*+-----------------------------------------------------------------------
X	icmd(cmd)
X This function implements the built in commands
X It returns non-zero if program should terminate
X------------------------------------------------------------------------*/
Xint
Xicmd(icmd_cmd)
Xregister char *icmd_cmd;
X{
X#define ARG_MAX_QUAN 40
Xchar *arg[ARG_MAX_QUAN];
Xchar cmd[128];
Xint itmp,itmp2,itmp3;
Xint token;
Xint narg = 0;
XESD *tesd;
Xchar s80[80];
Xchar *epoch_secs_to_str();
Xlong atol();
Xlong ltmp;
Xlong ltmp2;
Xchar *find_procedure();
Xchar *xon_status();
X
X	icmd_history_add(icmd_cmd);		/* add to history list */
X	strcpy(cmd,icmd_cmd);			/* get local copy of cmd string */
X	switch(cmd[0]) 
X	{
X		case '.':		/* exit xmtr() (generally program too) */
X			ff(se,"  disconnecting from line %s\r\n",shm->Lline);
X			return(1);
X		case '!':
X		case '$':
X		case '>':
X			ff(se,"\r\n");
X			shell(cmd);
X			return(0);
X		case '-':
X			ff(se,"\r\n");
X			exec_cmd(&cmd[1]);
X			return(0);
X		case '^':
X			ff(se,"\r\n");
X			phrase_help();
X			return(0);
X		case '?':
X			icmd_help(0,(char **)0);
X			return(0);
X#ifdef DEBUG_SHM_SCREEN
X		case '@':
X			{
X			FILE *fp = fopen("/tmp/screen","w");
X			int y,x;
X			char *cptr = (char *)shm->screen;
X				for(y = 0; y < 43; y++)
X				{
X					for(x = 0; x < 80; x++)
X						fputc(*cptr++,fp);
X					fputc('\n',fp);
X				}
X				fclose(fp);
X			}
X			break;
X#endif
X		default:
X			break;
X	}
X
X/* not single character argument */
X	build_arg_array(cmd,arg,ARG_MAX_QUAN,&narg);
X
X/* handle phrases */
X	if(isdigit(*arg[0]))
X	{
X		phrases(narg,arg);
X		return(0);
X	}
X
X/* search command list */
X	if((token = search_cmd_list(icmd_cmds,arg[0])) < 0)
X	{
X		ff(se,"\r\n");
X		if(find_procedure(arg[0]))
X		{
X			icmd_do_proc(narg,arg);
X			return(0);
X		}
X		ff(se,"no such command or procedure ... HOME ? for help\r\n");
X		return(0);
X	}
X
X	switch(token)
X	{					/* keep alphabetized PLEASE */
X		case CTrx:
X		case CTry:
X		case CTrz:
X		case CTrk:
X		case CTrs:
X		receive_files_from_remote(narg,arg);
X		break;
X
X		case CTsx:
X		case CTsy:
X		case CTsz:
X		case CTsk:
X		case CTss:
X		send_files_to_remote(narg,arg);
X		break;
X
X		case CTbaud:
X		if(narg == 1)
X			ff(se,"   baud rate is %u\r\n",shm->Lbaud);
X		else
X		{
X			itmp = atoi(arg[1]);
X			if(!lnew_baud_rate(itmp))
X				ff(se,"   baud rate set to %u\r\n",shm->Lbaud);
X			else
X			{
X				ff(se,"   invalid baud rate: %u\r\n",itmp);
X				ff(se,"valid rates: 110,300,600,1200,2400,4800,9600,19200\r\n");
X			}
X		}
X		break;
X
X		case CTbreak:
X		lbreak();
X		ff(se,"   break sent\r\n");
X		break;
X
X		case CTcd:
X		(void)change_directory(arg[1],(narg == 1) ? 0 : 1);
X		break;
X
X		case CTpushd:
X		(void)push_directory(arg[1],(narg == 1) ? 0 : 1,0);
X		break;
X
X		case CTpopd:
X		(void)pop_directory(arg[1],(narg == 1) ? 0 : 1,0);
X		break;
X
X		case CTclrx:
X		lclear_xmtr_xoff();
X		break;
X
X		case CTdial:
X		if(narg > 1)
X		{
X			ff(se,"\r\n");
X			strcpy(shm->Llogical,arg[1]);
X			if(!lookup_logical_telno())
X				ff(se,"No such entry. Try 'dial' with no arguments\r\n");
X			else
X			{
X				if(isdigit(shm->Llogical[0]) ||
X					!find_procedure(shm->Llogical))
X					DCE_dial();
X				else
X				{
X					arg[0] = shm->Llogical;
X					arg[1] = "!INTERACTIVE";
X					icmd_do_proc(2,arg);
X				}
X			}
X		}
X		else
X		{
X			pde_list_manager();
X		}
X		break;
X
X		case CTdo:
X		ff(se,"\r\n");
X		icmd_do_proc(narg - 1,&arg[1]);
X		break;
X
X		case CTptrace:
X		if(narg > 1)
X			proctrace = yes_or_no(arg[1]);
X		ff(se,"  procedure trace %s",proctrace ? "on" : "off");
X		if(proctrace > 1)
X			ff(se," (%d)",proctrace);
X		ff(se,"\r\n");
X		break;
X
X		case CTpcmd:
X		itmp = strlen(arg[0]);
X		if((tesd = esdalloc(256)) == (ESD *)0)
X		{
X			ff(se,"  no memory!!?!\r\n");
X			break;
X		}
X		strcpy(tesd->pb,icmd_cmd + itmp + 1);
X		tesd->cb = strlen(tesd->pb);
X		ff(se,"\r\n");
X		kill_rcvr_process(SIGUSR1);
X		ttymode(2);
X		if(itmp = execute_esd(tesd))
X		{
X			proc_error(itmp);
X			esdshow(tesd,"");
X		}
X		esdfree(tesd);
X		ttymode(1);
X		start_rcvr_process(0);
X		break;
X
X		case CTplog:
X		fputs("  ",se);
X		if(narg > 1)
X		{
X			if(!strcmp(arg[1],"off"))
X				plog_control((char *)0);
X			else
X				plog_control(arg[1]);
X		}
X		
X		if(plog_fp)
X			ff(se,"procedure logging: %s\r\n",plog_name->pb);
X		else
X			fputs("procedure logging off\r\n",se);
X		break;
X
X		case CTduplex:
X		if(narg > 1)
X		{
X			switch(to_lower(*arg[1]))
X			{
X				case 'f': shm->Lfull_duplex = 1; ff(se,"  now "); break;
X				case 'h': shm->Lfull_duplex = 0; ff(se,"  now "); break;
X				default: ff(se,
X"\r\nfirst argument character must be F or H for full or half duplex\r\n");
X					break;
X			}
X		}
X		else		/* no argument */
X			ff(se,"  currently ");
X
X		ff(se,"%s duplex\r\n",(shm->Lfull_duplex) ? "full" : "half");
X		break;
X
X		case CTexit:
X		ff(se,"  disconnecting from line %s\r\n",shm->Lline);
X		return(1);
X
X		case CTfi:
X		file_insert_to_line(narg,arg);
X		break;
X
X		case CThangup:
X		ff(se,"  hanging up ...\r\n");
X		DCE_hangup();
X#if defined(FASI)
X		{
X			uchar msr = fasi_msr();
X			ff(se,"hangup complete ... DCD is %s\r\n",
X				(msr & MS_DCD_PRESENT) ? "STILL HIGH" : "low");
X		}
X#else
X		ff(se,"hangup complete\r\n");
X#endif
X		break;
X
X		case CThelp:
X		icmd_help(narg,arg);
X		break;
X
X		case CTsdname:
X		if(narg > 1)
X		{
X		char *new_file_name;
X
X			itmp = 0;	/* do not need to free(new_file_name) */
X			if(find_shell_chars(arg[1]))
X			{
X
X				if(expand_wildcard_list(arg[1],&new_file_name))
X				{
X					ff(se,"  %s\r\n",new_file_name);
X					break;
X				}
X				itmp = 1;	/* need to free(new_file_name) */
X			}
X			else
X				new_file_name = arg[1];
X
X			screen_dump_file_name[0] = 0;
X			if(*new_file_name != '/')
X			{
X				strcpy(screen_dump_file_name,curr_dir);
X				strcat(screen_dump_file_name,"/");
X			}
X			strcat(screen_dump_file_name,arg[1]);
X			if(itmp)
X				free(new_file_name);
X		}
X		ff(se,"\r\nscreen dump name: %s\r\n",screen_dump_file_name);
X		break;
X
X		case CTlog:
X		case CTloff:
X		case CTllp:
X		icmd_log(token,narg,arg);
X		break;
X
X		case CTnl:
X		case CTnlin:
X		case CTnlout:
X		nlin_nlout_control(token,narg,arg);
X		break;
X
X		case CTparity:
X		if(narg > 1)
X		{
X			switch(to_lower(*arg[1]))
X			{
X				case 'n':
X					shm->Lparity = 0; break;
X				case 'e':
X				case 'o':
X					shm->Lparity = to_lower(*arg[1]); break;
X				default:
X					ff(se,"   unrecognized parity: %c\r\n",*arg[1]);
X					goto PARITY_DONE;
X			}
X			lset_parity(1);
X		}
X		ff(se,"   parity set to %c\r\n",
X			(shm->Lparity) ? to_upper(shm->Lparity) : 'N');
XPARITY_DONE: ;
X		break;
X
X		case CTpid:
X		ff(se,": xmtr %d, rcvr: %d, parent: %d, group: %d\r\n",
X			getpid(0),rcvr_pid,getppid(0),getpgrp(0));
X		break;
X
X		case CTpwd:
X		fputs(" ",se);
X		tcap_stand_out();
X		ff(se," %s ",curr_dir);
X		tcap_stand_end();
X		fputs("\r\n",se);
X		break;
X
X		case CTredial:
X		(void)DCE_redial(arg,narg);
X		break;
X
X		case CTrev:
X		fputs("\r\n",se);
X		fputs(hello_str,se);
X		fputs("\r\n",se);
X		ff(se,"%s\r\n",makedate);
X#if defined(WHT) && !defined(NOMEMCHECK)
X		_dump_malloc();
X#endif
X		break;
X
X		case CTtime:
X		get_tod(4,cmd);
X		ff(se,": %s\r\n",cmd);
X		break;
X
X		case CTts:
SHAR_EOF
true || echo 'restore of ecuicmd.c failed'
fi
echo 'End of ecu310 part 5'
echo 'File ecuicmd.c is continued in part 6'
echo 6 > _shar_seq_.tmp
exit 0
--------------------------------------------------------------------
Warren Tucker, TuckerWare emory!n4hgf!wht or wht@n4hgf.Mt-Park.GA.US
Hacker Extraordinaire  d' async PADs,  pods,  proteins and protocols

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.


