/* Main-level network program:
 * initialization
 * keyboard processing
 * generic user commands
 *
 * Copyright 1991 Phil Karn, KA9Q
 * Mods by KO4KS
 */
#include "global.h"
#include <fcntl.h>
#include "ctype.h"
#include "commands.h"
#if defined(MSDOS)
#include <conio.h>
#else
#include <time.h>
#endif
#ifdef UNIX
#include <sys/stat.h>
#include <signal.h>
#endif
#include <stdarg.h>
#include "mbuf.h"
#include "timer.h"
#include "proc.h"
#include "iface.h"
#include "udp.h"
#include "netrom.h"
#include "ftpcli.h"
#include "tty.h"
#include "usock.h"
#include "daemon.h"
#include "devparam.h"
#include "domain.h"
#include "mailbox.h"
#include "files.h"
#include "main.h"
#include "remote.h"
#include "trace.h"
#ifdef fprintf
#undef fprintf
#endif
#include "mailutil.h"
#include "smtp.h"
#include "assoc.h"
#ifdef SQL
#include "sql.h"
#endif
#ifdef CATALOG
#include "catalog.h"
#endif
#include "asy.h"
#ifdef MSDOS
#include "n8250.h"
#else
#include "unixasy.h"
#endif
#include "slip.h"
#include "nrs.h"

#ifdef	AXUDP
#include "axudp.h"
#endif

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: main.c,v 1.60 2001/05/14 17:49:53 brian Exp $";
#endif


#ifdef MSDOS
#undef  FA_DIREC
#define FA_DIREC 0x10
#endif

#ifdef SCREENSAVER
extern int32 LastIO;
#endif

#ifdef AX25
void reset_all_ax25 (void);
#endif

static char symname[] = "$Name:  $";

extern int Warnings;
static int CrashProtect = 0;


#ifdef LOG_GMT_TZ
#define TZTIME(x) asctime(gmtime(x))
#else
#define TZTIME(x) ctime(x)
#endif

#if defined(__bsdi__) || defined(__FreeBSD__)
extern char *tzname[2];
#endif

#ifdef BROWSER
static int UpdateTimeout = 30;
#endif

extern int getcolor (char *color);
extern int SYSback, SYSfore;

#ifdef PATCURSES
extern short STATback, STATfore, SPLITfore, SPLITback;
#endif

#ifdef RLINE
extern void ReadFwdBbs (void);
#endif
extern struct cmds Cmds[], Attab[];
#ifdef MSDOS
char vgaDesired = 0;
#endif
extern unsigned char SCREENlength, SCREENwidth;
extern void display_map (void);

extern short NextCmd;
extern struct session *ScreenOwner;	/* Session currently displayed */

#if	(!defined(MSDOS) || defined(ESCAPE))	/* PC uses F-10 key always */
static char Escape = 0x1d;	/* default escape character is ^] */
#endif

char NoRead[] = "Can't read %s: %s\n";
char Badhost[] = "Host '%s' unknown\n";
char Badinterface[] = "Interface \"%s\" unknown\n";
char Existingiface[] = "Interface %s already exists\n";
char Nospace[] = "No space!!\n";/* Generic malloc fail message */
char readmeFile[] = "features.new";
extern char Tnosversion[];
char Nosversion[] = "KA9Q NOS version %s\n%s";

static char MPRUNNINGerr[] = "\007*** Cannot resolve hostnames in the startup file!\n"
	"*** Try using the ip address (xx.xx.xx.xx) instead\n***\n"
	"*** The following command could NOT be completed:\n"
	"***     %s***\n\n";

#ifdef UNIX
static char LockName[] = "tnos.lock";
extern char const *LOCKDIR;
#endif

char Compiled[] = "Compiled"
#ifdef __GNUC__
	" with "
#ifdef MSDOS
	"DJGPP "
#else
	"GCC "
#endif
	 __VERSION__
#endif
	" on " __DATE__ " at " __TIME__ "\n";

static char Prompt[] = "Net";
char Noperm[] = "Permission denied.\n";
char Nosock[] = "Can't create socket\n";
static char FromSysop[] = "*** Msg from SYSOP: %s\n";
char SysMessage[] = "System message";
char *Hostname = NULLCHAR;
char *Motd = NULLCHAR;		/* Message Of The Day */
extern int Attended;
int ThirdParty = TRUE;		/* Allows 3rd party mail by default */
int main_exit = FALSE;		/* from main program (flag) */
int Mprunning = -1;		/* flag for other parts (domain) to signal
				 * that we are fully configured running. */
int Mprunning_err;
struct proc *Cmdpp;
#ifndef UNIX
struct proc *Display;
#endif

#ifdef	LZW
int Lzwmode = LZWCOMPACT;
int16 Lzwbits = LZWBITS;
#endif

#ifdef SCRIPTING
extern AarrayPtr SetVariables;
#endif

#ifdef MBFWD
extern int FWDmode;
#endif

#ifdef TRACE
int Tracesession = 1;
struct session *Trace = NULLSESSION;
#endif

#ifdef SOUNDS
int dosounddefine (int argc, char *argv[], void *p);
#endif

#ifdef AXIP
extern uint32 *axipaddr;
extern int AXIPlinks;
#endif

#ifdef AXUDP
/* 12Mar2001, VE4KLM */
extern AXUDPCFG *axudpcfg;
extern int AXUDPlinks;
#endif

extern int mbxMaxUsers;

static char PromptType;
static char *Customprompt;

#if defined(ALLCMD) && !defined(UNIX)
static char *DumpAddr = NULL;	/* Memory dump pointer */
#endif

FILE *Logfp;
time_t StartTime;		/* Time that NOS was started */
static int Verbose;
char *firstMsg;
int Numrows, Numcols;		/* screen size at startup - WG7J */
struct hist *Histry;		/* command recall stuff */
static int Histrysize = 0;
static int Maxhistory = 10;

#ifdef TNOS_68K
extern int Initroot;
extern int TNOS_68KAlarm;
extern short UseCurses;
#endif

extern int INStatline;
extern int BLOCKStatline;

#ifdef UNIX
static char **origargv;

#ifdef SETPSINFO
static char *LastArgv = NULL;          /* end of argv */
extern void fix_env (void);
extern int tnos_putenv (const char *envstr);
#endif
#endif

int chksession (struct session * sp);
extern void usercvt (void);
extern void setscreens (int back, int fore, int clr);

#if defined(ALLCMD) && !defined(UNIX)
static void ctohex (char *buf, int16 c);
static void fmtline (uint32 addr, char *buf, int16 len);
#endif

#ifdef MSDOS
extern void DOSinit (void);
extern void DOSterm (void);
#endif
extern void statLineToggle (int clearmarquee);
extern void statlog (const char *buf);
static void logcmd (char *);
static void freehistory (void);
static int _dowriteall (char *str);

void variable_expansion (struct mbuf ** bp);

#ifdef UNIX
# ifdef PATCURSES
#  define VALIDOPTS "A:a:B:D:d:Ef:g:k:l:mno:P:r:S:s:T:tU:u:Vv?w:x:y:z:"
# else
#  define VALIDOPTS "A:a:B:D:d:Ef:g:k:l:mno:P:r:S:s:T:tU:u:Vv?"
# endif
#endif

#ifdef MSDOS
#define VALIDOPTS "A:a:B:D:d:Eef:g:h:k:mno:P:r:s:U:u:Vv?"
#endif
#ifdef TNOS68K
#define VALIDOPTS "A:a:B:cD:d:Ef:g:k:mno:P:r:s:U:u:Vv?"
#endif


void
netPrompt ()
{
char const *str;

	switch (PromptType) {
		case 3:
			str = Customprompt;
			break;
		case 2:
			str = Command->curdirs->dir;
			break;
		case 1:
			if (Hostname) {
				str = Hostname;
				break;
			}	/* else, fall through */
		default:
			str = Prompt;
	}
	tprintf ("%s> ", str);
	usflush (Curproc->output);
}


int
main (int argc, char **argv, char **envp)
{
char *inbuff, *intmp;
FILE *fp;
struct daemon *tp;
struct mbuf *bp;
int i;
int c;
int did_init;
#ifdef UNIX
int setlockdir = 0;
int no_itimer = 0;
const char *trace_sm = 0;
const char *def_sm = 0;
static int oops = 0;
#else
long hinit = 102400;
struct text_info ti;
#endif
struct cur_dirs dirs;
int32 cmdcounter = 0;

#ifdef UNIX
	if (oops++) {
		iostop ();
		fprintf (stderr, "NOS PANIC: NOS main re-entered.\n");
		(void) fflush (stderr);
		(void) fflush (stdout);
		(void) kill (getpid (), 11);
	}
	origargv = argv;
#ifdef SETPSINFO
	while (*envp)	{
		(void) tnos_putenv (*envp);
		envp++;
	}
	LastArgv = envp[-1] + strlen(envp[-1]);

	fix_env ();
#endif
#endif
	StartTime = time ((time_t *)0);	/* NOS Start_Up time */
#ifdef MSDOS
	(void)
#endif
	SRANDOM ((long)((((unsigned int)getpid ()) << 16) ^ (unsigned long) StartTime));

#ifdef MSDOS
	/* Borland's library calls int10. Some vga mode utilities do not
	 * report the screen sizes correctly into the internal _video structure.
	 * This can cause the screen size to be faulty in the gettextinfo call.
	 * Instead, read the BIOS data area to get the correct screen info,
	 * and update the _video structure for later calls to
	 * gettextinfo(), clrscr(), etc... - WG7J
	 *
	 * If this doesn't work, you can now overwrite the values with
	 * the -r and -c command line options - WG7J
	 */
	gettextinfo (&ti);
	Numcols = ti.screenwidth;
	Numrows = ti.screenheight;

	if (Numrows == 1)
		Numrows = 25;
#endif
	list_filenames (1);
	did_init = 0;
	while ((c = getopt (argc, argv, VALIDOPTS)) != EOF) {
		switch (c) {
			case '?':	/* only display version info */
				printf (Nosversion, Version, Compiled);
				puts (Version2);
				exit (0);
				break;

			case 'A':	/* max number of SLIP/AX25 devices allowed */
				SLIP_MAX = atoi (optarg);
				break;

			case 'B':	/* max number of BBS processes allowed */
				NUMMBX = atoi (optarg);
				break;

#ifdef SCRIPTING
			case 'D':	/* define new environment variables */
				inbuff = strchr (optarg, '=');
				if (inbuff)
					*inbuff++ = 0;
				else
					inbuff = optarg;
				(void) assoc_addstr (&SetVariables, optarg, inbuff, 1);
				break;

			case 'E':	/* import environment variables */
				for (i = 0; envp[i] && *envp[i]; i++) {
					intmp = strdup (envp[i]);
					inbuff = strchr (intmp, '=');
					if (inbuff) {
						*inbuff++ = 0;
						(void) assoc_addstr (&SetVariables, intmp, inbuff, 1);
					}
					free (intmp);
				}
				break;
#endif

			case 'P':	/* max number of ASY port devices allowed */
				ASY_MAX = atoi (optarg);
				break;

#ifdef UNIX
			case 'S':
				if (sm_lookup (optarg, 0))
					def_sm = optarg;
				else
					printf ("No session manager \"%s\", using default\n", optarg);
				break;

			case 'T':
				if (sm_lookup (optarg, 0))
					trace_sm = optarg;
				else
					printf ("Session manager for trace not found, using default\n");
				break;
#endif

#ifdef BROWSER
			case 'U':	/* Auto update timeout */
				UpdateTimeout = atoi (optarg);
				break;
#endif

			case 'V':
				Verbose = 2;
				break;

#ifdef AXIP
			case 'a':	/* number of axip interfaces */
				AXIPlinks = atoi (optarg);
				break;
#endif

#ifdef TNOS_68K
			case 'c':	/* curses mode */
				UseCurses = 1;
				break;
#endif

			case 'd':	/* Root directory for various files */
				initroot (optarg);
				break;

#ifdef MSDOS
			case 'e':	/* EGA/VGA 43/50 mode desired */
				vgaDesired = 1;
				break;
#endif

			case 'f':	/* read a filename config file */
				assign_filenames (optarg, 1);
				did_init = 1;
				break;

			case 'g':	/* foreground color */
				i = getcolor (optarg);
				if (i != -1)
					SYSfore = i;
				break;

#ifdef MSDOS
			case 'h':	/* Heap initialization */
				hinit = atol(optarg);
				break;
#endif

			case 'k':	/* background color */
				i = getcolor (optarg);
#ifdef MSDOS
				i += 10;
#endif
				if (i != -1)
					SYSback = i;
				break;

#ifdef UNIX
			case 'l':	/* set LOCKDIR path */
				findUnixLockdir(optarg);
				setlockdir = 1;
				break;
#endif
			case 'm':	/* only display TNOS MAP info */
				display_map ();
				exit (0);
				break;

#ifdef TRACE
			case 'n':	/* No session for tracing */
				Tracesession = 0;
				break;
#endif

			case 'o':	/* override number of interactive sessions */
				Nsessions = (unsigned) atoi (optarg);
				if (Nsessions < 5)
					Nsessions = 5;	/* but not less than 5 */
				break;

#ifdef NETROM
			case 'r':	/* number of netrom open circuits */
				NR4MAXCIRC = atoi (optarg);
				break;
#endif

			case 's':	/* Number of sockets */
				Nusock = atoi (optarg);
				break;

#ifdef UNIX
			case 't':
				no_itimer = 1;	/* for debugging */
				break;
#endif

#ifdef AXUDP
			case 'u':	/* number of axudp interfaces */
				AXUDPlinks = atoi (optarg);
				break;
#endif

			case 'v':
				Verbose = 1;
				break;

#if defined(UNIX) && defined(PATCURSES)	/* _ve1ttl - w, x, y, z */
			case 'w':
				i = getcolor (optarg);
				if (i != -1)
					STATback = i;
				break;

			case 'x':
				i = getcolor (optarg);
				if (i != -1)
					STATfore = i;
				break;

			case 'y':
				i = getcolor (optarg);
				if (i != -1)
					SPLITback = i;
				break;

			case 'z':
				i = getcolor (optarg);
				if (i != -1)
					SPLITfore = i;
				break;
#endif

			default:
				break;
		}
	}
#ifdef UNIX
	if (!setlockdir)
		findUnixLockdir (NULLCHAR);
	{
		char *rp = mallocw (sizeof (rootdir) + 2 + sizeof (LockName));
		sprintf (rp, "%s/%s", rootdir, LockName);
		if (Unixlockfile (rp, 0, LockName, 1) == -1)	{
			printf ("Cannot start TNOS! Lockfile conflict!\n");
			(void) fflush (stdout);
			(void) sleep (5);
			exit (1);
		}
		free (rp);
	}
#endif
	if (SLIP_MAX > ASY_MAX)
		ASY_MAX = SLIP_MAX;

	Asy = (struct asy *) calloc ((unsigned) ASY_MAX, sizeof (struct asy));
	Slip = (struct slip *) calloc ((unsigned) SLIP_MAX, sizeof (struct slip));
#ifdef TIPMAIL
	Tipsuspended = (struct suspended *) calloc ((unsigned) ASY_MAX, sizeof (struct suspended));
#endif
#if defined(NETROM) && defined(NRS)
	Nrs = (struct nrs *) calloc ((unsigned) ASY_MAX, sizeof (struct nrs));
#endif
#ifdef AXIP
	axipaddr = calloc ((unsigned) AXIPlinks, sizeof (int32));
#endif
#ifdef AXUDP
	axudpcfg = calloc ((unsigned) AXUDPlinks, sizeof (AXUDPCFG));
#endif
	Mbox = (struct mbx **) calloc ((unsigned) NUMMBX, sizeof (struct mbx *));

	mbxMaxUsers = NUMMBX;
#ifdef MBFWD
	subchannels = (struct subchan *) calloc ((unsigned) NUMMBX, sizeof (struct subchan));

#endif
#ifdef NETROM
	Nr4circuits = calloc ((unsigned) NR4MAXCIRC, sizeof (struct nr4circp));

#endif

#if defined(MSDOS)
	DOSinit ();
#endif
#ifdef TNOS_68K
	if (!Initroot)
		initroot ("/dd/nos");
	oskinit ();
#ifdef TRACE
	Tracesession = UseCurses;	/* no session trace if dumb terminal mode */
#endif
#endif
	intmp = firstMsg;	/* we won't report an error here, if it occurs */
	firstMsg = (char *) 0;
	if (!did_init)
#ifdef UNIX
		assign_filenames (".nosrc", 1);
#else
		assign_filenames ("config.nos", 0);	/* automatically look for users config */
#endif
	if (firstMsg)
		free (firstMsg);
	firstMsg = intmp;
	kinit ();
	ipinit ();
#ifdef UNIX
	ioinit (no_itimer);
#else
#ifdef MSDOS
	ioinit (hinit);
#endif
#endif
#ifdef SQL
	sql_init ();
#endif
	list_filenames (0);
#ifdef MAILBOX
	{
		char tmpbuf[128];

		sprintf (tmpbuf, "%s/control", Mailspool);
		(void) mkdir (tmpbuf, 0777);
		sprintf (tmpbuf, "%s/users", Mailspool);
		(void) mkdir (tmpbuf, 0777);
	}
#endif
#ifdef AX25
	memcpy (Mycall, NOCALL, AXALEN);
	memcpy (AXuser, NOCALL, AXALEN);
#endif
	sockinit ();
	Cmdpp = mainproc ("cmdintrp");

	Sessions = (struct session *) callocw (Nsessions, sizeof (struct session));

	for (i = 0; (unsigned) i < Nsessions; i++)
		Sessions[i].index = i;

#ifdef TRACE
	if (Tracesession)
#ifdef UNIX
	{
		if (!trace_sm)
			trace_sm = Trace_sessmgr;
		Trace = sm_newsession (trace_sm, NULLCHAR, TRACESESSION, 0);
	}
#else
		Trace = newsession (NULLCHAR, TRACESESSION, 0);
#endif
#endif
#ifdef UNIX
	if (!def_sm)
		def_sm = Command_sessmgr;
	ScreenOwner = Command = Lastcurr = sm_newsession (def_sm, NULLCHAR, COMMAND, 0);
#else
	ScreenOwner = Command = Lastcurr = newsession (NULLCHAR, COMMAND, 0);	/*"command int" not needed*/
#endif
	/* Flow mode is set AFTER we've read the autoexec file !
	 * this keeps systems from locking up if the reboot is unattended - WG7J
	 */
#ifndef UNIX
	Display = newproc ("display", 1024, display, 0, NULLCHAR, NULL, 0);
#endif
	setscreens (0, 0, 1);
	if (firstMsg) {
		tputs (firstMsg);
		free (firstMsg);
	}
	tputs (Tnosversion);
	tputs (Version2);
	tputs ("Copyright 1991 by Phil Karn (KA9Q), Brian A. Lantz (KO4KS) and contributors.\n");
#ifdef MSDOS
	(void) _get_dos_version (1);
	tprintf ("Running under %s - Version %d.%d\n", _os_flavor, _osmajor, _osminor);
#endif
#ifdef RLINE
	ReadFwdBbs ();
#endif
#ifdef CATALOG
	/* this serves two purposes. First, it will error if file not found
	   NOW rather than later. Second, it makes the OWNER of the open file
	   be the Command Session. If this wasn't done (and a user process
	   opened the file later) then the file is closed when the process
	   dies, which confuses TNOS totally!
	 */
	switch (catalog_check ()) {
		case -1:
			tputs ("\nImproperly formatted etc/catalog.cat file!!\nIt looks corrupted. Unless you fix this, a crash is probable!\n\n\007");
			break;
		case 1:
			tputs ("\nVersion of the etc/catalog.cat file does NOT match this executable!\nA crash is possible!\n\n\007");
			break;
		case 0:	/* nothing to do, it looks fine */
		default:
			break;
	}
#endif

#ifdef SOUNDS			/* pre-define the 'chat' sound */
	{
		char const *myargv[4];

		myargv[1] = "chat";
		myargv[2] = "";
		myargv[3] = "used to indicate an incoming chat session";
		(void) dosounddefine (4, (char **) myargv, (void *) 0);
	}
#endif

#ifdef USERLOG
	/* a special sanity check, in case TNOS dies in the midst of
	   updating the users.dat file */
	{
		long dat, bak;
		char *tmpcp;

		tmpcp = strdup (UDefaults);
		dat = fsize (tmpcp);
		free (tmpcp);
		tmpcp = strdup (UDefbak);
		bak = fsize (tmpcp);
		free (tmpcp);
		if (bak > dat && (bak - dat) > 80L) {
			unlink (UDefaults);
			(void) rename (UDefbak, UDefaults);
		}
	}
#endif

	rflush ();

#ifdef UNIX
	SCREENlength = uchar(Numrows);
	SCREENwidth = uchar(Numcols);
#endif

	usercvt ();
	/* Start background Daemons */
	for (tp = Daemons;; tp++) {
		if (tp->name == NULLCHAR)
			break;
		(void) newproc (tp->name, tp->stksize, tp->fp, 0, NULLCHAR, NULL, 0);
	}

	(void) init_dirs (&dirs);
	Command->curdirs = &dirs;

	Mprunning = 0;

	if (!access (readmeFile, 4)) {
		int row;
		char *args[2];

		args[1] = readmeFile;
		tprintf ("\nWould you like to view the '%s' file, containing information\non the new features for this version of TNOS?\n", readmeFile);
		Command->ttystate.edit = Command->ttystate.echo = 0;
		row = keywait ("Yes or No [y/n]? ", 0);
		Command->ttystate.edit = Command->ttystate.echo = 1;
		if (row == 'Y' || row == 'y')
			(void) doview (2, args, 0);
		tputc ('\n');
	}
	if (optind < argc) {
		/* Read startup file named on command line */
		if ((fp = fopen (argv[optind], READ_TEXT)) == NULLFILE)
			tprintf (NoRead, argv[optind], sys_errlist[errno]);
	} else {
		/* Read default startup file named in files.c (autoexec.nos) */
		if ((fp = fopen (Startup, READ_TEXT)) == NULLFILE)
			tprintf (NoRead, Startup, sys_errlist[errno]);
	}
	if (fp != NULLFILE) {
		char *bptr;

		inbuff = mallocw (BUFSIZ);
		intmp = mallocw (BUFSIZ);
		while (fgets (inbuff, BUFSIZ, fp) != NULLCHAR) {
			kwait (NULL);
			strncpy (intmp, inbuff, BUFSIZ);
			if (Verbose == 1) {
				tprintf ("%s", intmp);
				tflush ();
			} else if (Verbose == 2) {
				tprintf ("%s  Execute (*Y/n/all)", intmp);
				tflush ();
				if (recv_mbuf (Command->input, &bp, 0, NULLCHAR, 0) != -1) {
					c = toupper (*bp->data);
					free_p (bp);
					if (c == 'N')
						continue;
					if (c == 'A')
						Verbose = 1;
				}
			}
			Mprunning_err = 0;
			bptr = _variable_expansion (strdup (inbuff));
			if (cmdparse (Cmds, bptr, NULL) != 0) {
				tprintf ("input line: %s", intmp);
				rflush ();
			}
			free (bptr);
			if (Mprunning_err)
				tprintf (MPRUNNINGerr, intmp);
		}
		(void) fclose (fp);
		free (inbuff);
		free (intmp);
	}
	Mprunning = 1;		/* we are on speed now */
#if 0
	Command->flowmode = 1;	/* set 'more' paging on command screen */
#endif

	if (Warnings)
		(void) dowarnings (0, NULLCHARP, (void *) 0);
	reg_status ();

#ifdef BROWSER
	if (UpdateTimeout)	{
		char const *myargv[3];
		char numbuf[10], url[60];

		tprintf ("\nAutosearching for updates at lantz.com - waiting maximum of %d seconds\n"
			 "To avoid this in the future, add '-U 0' to your TNOS commandline\n",
			 UpdateTimeout);

		sprintf (numbuf, "%d", UpdateTimeout);
		myargv[1] = numbuf;
		strcpy (url, "http://www.lantz.com/tnos/updates.html?NOT");
		if (is_registered())
			strcpy (url, "http://www.lantz.com/tnos/updates.html");
		myargv[2] = url;
		kwait (NULL);
		(void) dobrowsercheck (3, (char **) myargv, (void *) 0);
	}
#endif
	/* Now loop forever, processing commands */
	for (;;) {
		netPrompt ();
		if (recv_mbuf (Command->input, &bp, 0, NULLCHAR, 0) != -1) {
			logcmd ((char *) bp->data);
			variable_expansion (&bp);
			(void) cmdparse (Cmds, (char *) bp->data, Lastcurr);
			free_p (bp);

		}
		if (Warnings && !(++cmdcounter % 20))
			(void) dowarnings (0, NULLCHARP, (void *) 0);
	}
}



int
chksession (struct session *sp)
{
#ifdef TRACE
	return (((sp->type != COMMAND) && (sp != Trace) && (sp->type != FREE)) ? 1 : 0);
#else
	return (((sp->type != COMMAND) && (sp->type != FREE)) ? 1 : 0);
#endif
}



/* Keyboard input process */
/* Modified to support F-key session switching,
 * from the WNOS3 sources - WG7J
 */
void
keyboard (int i OPTIONAL, void *v1 OPTIONAL, void *v2 OPTIONAL)
{
int c;
struct mbuf *bp;
register int j, k;
struct session *sp;
int got = 0;

	/* Keyboard process loop */
	for (;;) {
		c = kbread ();
#ifdef MSDOS
		if (c == -1)
			continue;
#endif
#ifdef SCREENSAVER
		LastIO = secclock();
		if (ssenabled())
			screensaver ();
#endif
		
#if (!defined(MSDOS) || defined(ESCAPE))
		if (c == Escape && Escape != 0)
			c = -2;
#endif
		if (c == -2 && Current != Command) {
			/* Save current tty mode and set cooked */
			/*			swapscreen(Current,Command); */
			Lastcurr = Current;
			Current = Command;
			swapscreen (Lastcurr, Current);

#if 0
			/* set 'more' paging on command screen */
			Command->flowmode = 1;
#endif
		}
		if (c < -2) {	/* F1 to F9 (or INSERT) pressed */
			switch (c) {
#ifdef TRACE
				case -11:	/* If F9 is pressed, -11 is returned and we swap to Trace - WG7J */
					if (Tracesession) {
						if (Current != Trace) {
							/* Save current tty mode and set cooked */
							/*						swapscreen(Current,Trace); */
							BLOCKStatline = 1;
							while (INStatline)
								kwait (NULL);
							Lastcurr = Current;
							Current = Trace;
							swapscreen (Lastcurr, Current);
							BLOCKStatline = 0;

							/* turn off 'more' paging on trace screen */
							Trace->flowmode = 0;
						} else {
							/* Toggle back to previous session */
							/*						swapscreen(Trace,Lastcurr); */
							BLOCKStatline = 1;
							while (INStatline)
								kwait (NULL);
							if (Lastcurr == NULLSESSION)
								Current = Command;
							else
								Current = Lastcurr;
							Lastcurr = Trace;
							swapscreen (Lastcurr, Current);
							BLOCKStatline = 0;
						}
					}
					break;
#endif
				case -105:	/* INSERT pressed, statline toggle */
					statLineToggle (1);
					break;
				case -106:	/* DEL pressed, toggle flow control */
					Current->flowmode ^= 1;
					break;
				case -107:	/* HOME pressed, kick current session */
					(void) dokick (0, 0, Current);
					break;
				case -108:	/* END pressed, end current session */
					(void) doreset (0, 0, Current);
					break;
				case -109:	/* PGUP pressed, goto previous session */
					j = (int) (((Current->index - 1) > 0) ? Current->index - 1 : (int) Nsessions - 1);
					for (sp = &Sessions[j]; sp > Sessions; sp--)
						if ((got = chksession (sp)) != 0)
							break;
					if (!got)
						for (sp = &Sessions[Nsessions - 1]; sp > Current; sp--)
							if ((got = chksession (sp)) != 0)
								break;
					if (got) {
						/*					swapscreen(Current,sp); */
						Lastcurr = Current;
						Current = sp;
						swapscreen (Lastcurr, Current);
					}
					break;
				case -110:	/* PGDN pressed, goto next session */
					for (sp = &Sessions[Current->index + 1]; sp < &Sessions[Nsessions]; sp++)
						if ((got = chksession (sp)) != 0)
							break;
					if (!got)
						for (sp = Sessions; sp < Current; sp++)
							if ((got = chksession (sp)) != 0)
								break;
					if (got) {
						/*					swapscreen(Current,sp); */
						Lastcurr = Current;
						Current = sp;
						swapscreen (Lastcurr, Current);
					}
					break;
				default:
					k = (-1 * c) - 2;
					for (sp = Sessions, j = 0; sp < &Sessions[Nsessions]; sp++) {
						if (sp->type == COMMAND)
							continue;
						j++;
						if (sp->type != FREE && j == k) {
							/*						swapscreen(Current,sp); */
							Lastcurr = Current;
							Current = sp;
							swapscreen (Lastcurr, Current);
							break;
						}
					}
			}
		}
		/*		Current->row = MOREROWS; */
		Current->row = SCREENlength - 1 - (Current->split * 2);
		ksignal (&Current->row, 1);
		if (c >= 0) {
#ifdef UNIX
			if (Current->morewait)	/* end display pause, if any */
				Current->morewait = 2;
#endif
			/* If the screen driver was in morewait state, this char
			 * has woken him up. Toss it so it doesn't also get taken
			 * as normal input. If the char was a command escape,
			 * however, it will be accepted; this gives the user
			 * a way out of lengthy output.
			 */
			if (!Current->morewait && (bp = ttydriv (Current, c)) != NULLBUF)
				(void) send_mbuf (Current->input, bp, 0, NULLCHAR, 0);
		}
	}
}


#ifdef LOCK
extern int Kblocked;
extern char *Kbpasswd;


/*Lock the keyboard*/
int
dolock (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc == 1) {
		if (Kbpasswd == NULLCHAR)
			tputs ("Set password first\n");
		else {
			Kblocked = 1;
			tputs ("Keyboard locked\n");
			Command->ttystate.echo = 0;	/* Turn input echoing off! */
		}
		return 0;
	}
	if (argc == 3) {
		if (*argv[1] == 'p') {	/*set the password*/
			if (Kbpasswd != NULLCHAR) {
				free (Kbpasswd);
				Kbpasswd = NULLCHAR;	/* reset the pointer */
			}
			if (!strlen (argv[2]))
				return 0;	/* clearing the buffer */
			Kbpasswd = strdup (argv[2]);
			return 0;
		}
	}
	tputs ("Usage: lock password \"<unlock password>\"\nor    'lock' to lock the keyboard\n");

	return 0;
}



/*Unlock the keyboard remotely*/
int
dounlock (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc == 1)
		tputs ("Usage: unlock \"<unlock password>\"\n to unlock the keyboard\n");
	else if (!stricmp (Kbpasswd, argv[1])) {
		Kblocked = 0;
		tputs ("Keyboard unlocked\n");
		Command->ttystate.echo = 1;	/* Turn input echoing off! */
	}
	return 0;
}

#endif



/* Choose the prompt type */
int
doprompt (int argc, char *argv[], void *p OPTIONAL)
{
char const *str;

	if (argc == 1) {
		switch (PromptType) {
			case 3:
				str = "Custom";
				break;
			case 2:
				str = "Dirname";
				break;
			case 1:
				str = "Hostname";
				break;
			default:
				str = "Normal";
		}
		tprintf ("Prompt: %s %c %s\n", str, (PromptType == 3) ? '-' : ' ', (PromptType == 3) ? Customprompt : "");
		return 0;
	}
	switch (*argv[1]) {
		case 'n':
			PromptType = 0;	/* use the normal prompt */
			break;
		case 'h':
			PromptType = 1;	/* use the hostname */
			break;
		case 'd':
			PromptType = 2;	/* use the dirname */
			break;
		case 'c':
			PromptType = 3;	/* set a custom prompt */
			break;
		default:
			tputs ("Usage: prompt [hostname | dirname | normal | custom 'promptstr']\n");
			return 0;
	}
	if (Customprompt != NULLCHAR) {
		free (Customprompt);
		Customprompt = NULLCHAR;
	}
	if (PromptType == 3)
		Customprompt = strdup (argv[2]);
	return 0;
}



#ifdef ALLCMD
/* Standard commands called from main */
int
dodelete (int argc, char *argv[], void *p OPTIONAL)
{
int i;
char filenm[13], *cp, fullname[128];
char fname[128];
#ifdef DOS_GETFILEATTR
unsigned attr;
#endif

	for (i = 1; i < argc; i++) {
		strncpy (fname, make_fname (Command->curdirs->dir, argv[i]), 128);
#ifdef DOS_GETFILEATTR
		if (!_dos_getfileattr (fname, &attr) && (attr & FA_DIREC)) {
			strcat (fname, "/*.*");
			tputs ("All files in directory will be deleted!\nAre you sure (Y/N)? ");
			usflush (Curproc->output);
			if (recvline (Curproc->input, (unsigned char *) filenm, 13) == -1)
				break;
			if (toupper (*filenm) != 'Y')
				continue;
			cp = (char *) 1;
		}
#endif
		(void) filedir (fname, 0, filenm);
		if ((cp = strrchr (fname, '/')) != NULLCHAR)
			*cp = 0;
		for (; *filenm; (void) filedir (fname, 1, filenm)) {
			if (*filenm == '.' && (!filenm[1] || (filenm[1] == '.' && !filenm[2])))
				continue;
			sprintf (fullname, "%s%s%s", (cp) ? fname : "", (cp) ? "/" : "", filenm);
			if (unlink (fullname) == -1)
				tprintf ("Can't delete %s: %s\n", fullname, sys_errlist[errno]);
		}
	}
	return 0;
}



int
dorename (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
{
char fname1[128];
char fname2[128];

	strncpy (fname1, make_fname (Command->curdirs->dir, argv[1]), 128);
	strncpy (fname2, make_fname (Command->curdirs->dir, argv[2]), 128);
	if (rename (fname1, fname2) == -1)
		tprintf ("Can't rename: %s\n", sys_errlist[errno]);
	return 0;
}



int
docopy (int argc, char *argv[], void *p OPTIONAL)
{
register FILE *old, *new;
register int ch;
register unsigned char count;
char filenm[13], *dirnam = NULL, *cp, fullname[512];
int usedir = 0;
char fname[128], fname2[128];
#ifdef DOS_GETFILEATTR
unsigned attr;
#endif

	if (argc > 2) {
		strncpy (fname2, make_fname (Command->curdirs->dir, argv[2]), 128);
#ifdef DOS_GETFILEATTR
		(void) _dos_getfileattr (fname2, &attr);
		if (attr & FA_DIREC) {
			usedir = 1;
			dirnam = fname2;
		}
#endif
	} else
		usedir = 1;
	strncpy (fname, make_fname (Command->curdirs->dir, argv[1]), 128);
#ifdef DOS_GETFILEATTR
	if (!_dos_getfileattr (fname, &attr) && (attr & FA_DIREC)) {
		strcat (fname, "/*.*");
		cp = (char *) 1;
	}
#endif
	(void) filedir (fname, 0, filenm);
	if ((cp = strrchr (fname, '/')) != NULLCHAR)
		*cp = 0;
	for (; *filenm; (void) filedir (fname, 1, filenm)) {
		if (*filenm == '.' && (!filenm[1] || (filenm[1] == '.' && !filenm[2])))
			continue;
		sprintf (fullname, "%s%s%s", (cp) ? fname : "", (cp) ? "/" : "", filenm);
		if ((old = fopen (fullname, READ_BINARY)) == NULL) {
			tprintf ("Can't open %s: %s\n", fullname, sys_errlist[errno]);
			continue;
		}
		sprintf (fullname, "%s%s%s", (dirnam) ? dirnam : "", (dirnam) ? "/" : "", (usedir) ? filenm : fname2);
		if ((new = fopen (fullname, WRITE_BINARY)) == NULL) {
			tprintf ("Can't open %s: %s\n", fullname, sys_errlist[errno]);
			(void) fclose (old);
			continue;
		}
		(void) strlwr (fullname);
		tprintf ("Copying '%s'...\n", fullname);
		/* Now go copy */
		count = 0;
		while ((ch = fgetc (old)) != EOF) {
			if (fputc (ch, new) == EOF) {
				tputs ("Copy failed!\n");
				break;
			}
			if (!(++count))	/* be polite to other users */
				kwait (NULL);
		}
		(void) fclose (old);
		(void) fclose (new);
		if (!usedir)
			break;
	}
	return 0;
}

#endif /*ALLCMD*/



/*this is also called from the remote-server for the 'exit' command - WG7J*/
void
where_outta_here (resetme, where)
int resetme;			/* resetme is 0=exit, 1=low memory, 2=reset (if possible), 3=free lockup error, 5=proc loop error */
char const *where;
{
time_t StopTime;
FILE *fp;
char *inbuff, *intmp;
char *bptr;

	tcmdprintf ("*** Exiting TNOS...\n");
	if (NextCmd)
		NextCmd = 1;
#if 0
	inbuff = NextCmdMsg;
	NextCmdMsg = NULLCHAR;
	free (inbuff);
#endif

	/* Execute sequence of commands taken from file "~/onexit.nos" */
	/* From iw0cnb */
	/* only if we got here for a reason OTHER than low memory!! ko4ks */
	if ((resetme & 1) != 1 && (fp = fopen (Onexit, READ_TEXT)) != NULLFILE) {
		inbuff = mallocw (BUFSIZ);
		intmp = mallocw (BUFSIZ);
		while (fgets (inbuff, BUFSIZ, fp) != NULLCHAR) {
			strncpy (intmp, inbuff, BUFSIZ);
			if (Verbose) {
				tprintf ("%s", intmp);
				rflush ();
			}
			bptr = _variable_expansion (strdup (inbuff));
			if (cmdparse (Cmds, bptr, NULL) != 0)	{
				tprintf ("input line: %s", intmp);
				rflush ();
			}
			free (bptr);
		}
		(void) fclose (fp);
		free (inbuff);
		free (intmp);
	}
	StopTime = time (&StopTime);
	main_exit = TRUE;	/* let everyone know we're out of here */
#ifdef AX25
#ifdef MBFWD
	FWDmode = 0;		/* disable future forwarding sessions */
#endif
	if (resetme != 3 && resetme != 5)	{
		reset_all_ax25 ();
		kpause (2000);
	}
#endif
	reset_all ();
	if (Dfile_updater != NULLPROC)
		alert (Dfile_updater, 0);	/* don't wait for timeout */

	kpause (1000);	/* Allow tasks some time to complete */
#ifdef TRACE
	shuttrace ();
#endif
#if defined(MSDOS)
	DOSterm ();
#endif
	log (-1, "TNOS was stopped from '%s' with value %d", where, resetme);
#ifdef STATS
	if (!resetme)
		(void) doSTATsave (0, (char **) 0, (void *) 0);
#endif
	if (Logfp) {
		(void) fclose (Logfp);
		Logfp = NULLFILE;
	}
#ifdef UNIX
	detach_all_asy ();	/* make sure everything is unlocked */
	kwait (NULL);
#endif
	iostop ();
	freehistory ();
	free_dirs (Command->curdirs);
#ifdef TNOS_68K
	alm_delete (TNOS_68KAlarm);
#endif

#ifdef UNIX
#if 0
	if (resetme && resetme != 1) {
		int pid;

		if ((pid = fork ()) == 0)
			abort ();
		/* this SHOULD take care of clearing vombie processes */
		waitpid (pid, NULL, WNOHANG);
		execvp (origargv[0], origargv);	/* re-run NOS */
	}
#endif
	{
		char *rp = mallocw (sizeof (rootdir) + 2 + sizeof (LockName));
		sprintf (rp, "%s/%s", rootdir, LockName);
		(void) unlink (rp);
		free (rp);
	}
#else
	if (resetme)
		sysreset ();
#endif
	exit (0);
}



int
doexit (int argc, char *argv[], void *p OPTIONAL)
{
int c;
struct mbuf *bp;
time_t nowtime, elapsedtime;
unsigned int days, hrs, mins, secs;

	if (strnicmp (Curproc->name, "at ", 3) == 0 ||
	    strnicmp (Curproc->name, "Cron ", 5) == 0)
		where_outta_here (0, "doexit");

	if (Curproc->input != Command->input)
		return -2;	/*probably mailbox-sysop */
	if (argc == 2)
		c = argv[1][0];
	else {
		tputs ("\007Exit TNOS: Are you sure? ");
		tflush ();
		(void) recv_mbuf (Command->input, &bp, 0, NULLCHAR, 0);
		c = bp->data[0];
	}
	if (c != 'y' && c != 'Y') {
		Command->ttystate.edit = Command->ttystate.echo = 1;
		return 0;	/* signal delete of message */
	}
	nowtime = time (&nowtime);	/* current time */
	elapsedtime = nowtime - StartTime;	/* nos elapsed time */
	secs = (unsigned int) ((long) elapsedtime % 60);
	elapsedtime = elapsedtime / 60;
	mins = (unsigned int) ((long) elapsedtime % 60);
	elapsedtime = elapsedtime / 60;
	hrs = (unsigned int) ((long) elapsedtime % 24);
	elapsedtime = elapsedtime / 24;
	days = (unsigned int) (long) elapsedtime;
	tprintf ("TNOS Exiting - Runtime => %u days:%02u hours:%02u minutes:%02u seconds.\n", days, hrs, mins, secs);
	tflush ();
	kpause (1500);
	where_outta_here (0, "doexit");	/*No reset!*/
#if !defined(_lint)
	return 0;	/* to silence compiler warning */
#endif
}


extern char Chostname[], CConsole[];

#define CNAMELEN 16



int
dohostname (int argc, char *argv[], void *p OPTIONAL)
{
#ifdef CONVERS
char *cp;
#endif

	if (argc < 2) {
		if (Hostname)
			tprintf ("%s\n", Hostname);
	} else {
		struct iface *ifp;
		char *name;

		if ((ifp = if_lookup (argv[1])) != NULLIF) {
			if ((name = resolve_a (ifp->addr, FALSE)) == NULLCHAR) {
				tputs ("Interface address not resolved\n");
				return 1;
			} else {
				if (Hostname != NULLCHAR)
					free (Hostname);
				Hostname = name;
				tprintf ("Hostname set to %s\n", name);
			}
		} else {
			if (Hostname != NULLCHAR)
				free (Hostname);
			Hostname = strdup (argv[1]);
			/* Remove trailing dot */
			if (Hostname[strlen (Hostname) - 1] == '.')
				Hostname[strlen (Hostname) - 1] = '\0';
		}
#ifdef CONVERS
		/* If convers hostname not set yet, set it to first 10 chars
		 * of the hostname. If there are '.' from the right, cut off
		 * before that. - WG7J
		 */
		if (Chostname[0] == '\0') {
			strncpy (Chostname, Hostname, CNAMELEN);
			if ((cp = strrchr (Chostname, '.')) != NULLCHAR)
				*cp = '\0';
		}
		if (CConsole[0] == '\0') {
			strncpy (CConsole, Hostname, CNAMELEN);
			if ((cp = strchr (CConsole, '.')) != NULLCHAR)
				*cp = '\0';
		}
#endif
	}
	if (Hostname && strchr (Hostname, '.') == NULLCHAR)
		tprintf ("Warning - hostname should include domain\n");
	return 0;
}



int
dolog (int argc, char *argv[], void *p OPTIONAL)
{
static char *logname = NULLCHAR;
char fname[128];

	if (argc < 2) {
		if (Logfp && logname != NULLCHAR)
			tprintf ("Logging to %s\n", logname);	/* lint !e727 */
		else
			tputs ("Logging off\n");
		return 0;
	}
	if (Logfp) {
		log (-1, "TNOS log closed");
		(void) fclose (Logfp);
		Logfp = NULLFILE;
		free (logname);
		logname = NULLCHAR;
	}
	if (strcmp (argv[1], "stop") && strcmp (argv[1], "off")) {
		strncpy (fname, make_fname (Command->curdirs->dir, argv[1]), 128);
		logname = strdup (fname);
		Logfp = fopen (logname, APPEND_TEXT);
		log (-1, "TNOS v%s was started at %s", Version, TZTIME (&StartTime));
	}
	return 0;
}



/* Attach an interface
 * Syntax: attach <hw type> <I/O address> <vector> <mode> <label> <bufsize> [<speed>]
 */
int
doattach (int argc, char *argv[], void *p)
{
	return subcmd (Attab, argc, argv, p);
}



/* Manipulate I/O device parameters */
int
doparam (int argc, char *argv[], void *p OPTIONAL)
{
int param, set;
int32 val;
register struct iface *ifp;

	if ((ifp = if_lookup (argv[1])) == NULLIF) {
		tprintf (Badinterface, argv[1]);
		return 1;
	}
	if (ifp->ioctl == NULL) {
		tputs ("Not supported\n");
		return 1;
	}
	if (argc < 3) {
		for (param = 1; param <= 16; param++) {
			val = (*ifp->ioctl) (ifp, param, FALSE, 0L);
			if (val != -1)
				tprintf ("%s: %ld\n", parmname (param), val);
		}
		return 0;
	}
	param = devparam (argv[2]);
	if (param == -1) {
		tprintf ("Unknown parameter %s\n", argv[2]);
		return 1;
	}
	if (argc < 4) {
		set = FALSE;
		val = 0L;
	} else {
		set = TRUE;
		val = atol (argv[3]);
	}
	val = (*ifp->ioctl) (ifp, param, set, val);
	if (val == -1)
		tprintf ("Parameter %s not supported\n", argv[2]);
	else
		tprintf ("%s: %ld\n", parmname (param), val);
	return 0;
}



/* Display or set IP interface control flags */
int
domode (int argc, char *argv[], void *p OPTIONAL)
{
register struct iface *ifp;

	if ((ifp = if_lookup (argv[1])) == NULLIF) {
		tprintf (Badinterface, argv[1]);
		return 1;
	}
	if (argc < 3) {
		tprintf ("%s: %s\n", ifp->name,
			(ifp->flags & CONNECT_MODE) ? "VC mode" : "Datagram mode");
		return 0;
	}
	switch (argv[2][0]) {
		case 'v':
		case 'c':
		case 'V':
		case 'C':
			ifp->flags |= CONNECT_MODE;
			break;
		case 'd':
		case 'D':
			ifp->flags &= ~CONNECT_MODE;
			break;
		default:
			tprintf ("Usage: %s [vc | datagram]\n", argv[0]);
			return 1;
	}
	return 0;
}



#if	(!defined(MSDOS) || defined(ESCAPE))
int
doescape (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc < 2)
		tprintf ("0x%x\n", Escape);
	else
		Escape = (char) htoi (argv[1]);
	return 0;
}
#endif /* MSDOS */



/* Generate system command packet. Synopsis:
 * remote [-p <port#>] [-a <kickaddress>] <hostname> kickme
 * remote [-p <port#>] -k <key> <hostname> reset|exit
 * remote [-p <port#>] -k <key> -r <destIPaddr>[/<bits>] <hostname> add|drop
 * remote -s <password>
 * remote -g <gatewaypassword>
 */
int
doremote (int argc, char *argv[], void *p OPTIONAL)
{
struct sockaddr_in fsock;
int s, c;
char *data, x;
int16 port, len;
char *key = NULLCHAR;
int klen = 0;
uint32 addr = 0;
char *cmd, *host;
char *route = NULLCHAR;

	port = IPPORT_REMOTE;	/* Set default */
	optind = 0;		/* reinit getopt() */
	while ((c = getopt (argc, argv, "a:r:g:p:k:s:x:")) != EOF) {
		switch (c) {
			case 'a':
				if ((addr = resolve (optarg)) == 0) {
					tprintf (Badhost, optarg);
					return -1;
				}
				break;
			case 'p':
				port = (int16) atoi (optarg);
				break;
			case 'k':
				key = optarg;
				klen = (int) strlen (key);
				break;
			case 's':
				Rempass = strdup (optarg);
				return 0;	/* Only set local password */
#ifdef ENCAP
			case 'r':
				route = optarg;
				break;
			case 'g':
				RemRTpass = strdup (optarg);
				return 0;	/* Only set local password for adding routes */
#else
			case 'r':
			case 'g':
				tputs ("This option is not available to you. TNOS was NOT compiled\nwith support for the ENCAP features!\n");
				/* and fall through */
#endif
			default:
				return 0;
		}
	}
	if (optind > argc - 2) {
		tputs ("Insufficient args\n");
		return -1;
	}
	host = argv[optind];
	cmd = argv[optind + 1];
	if ((s = socket (AF_INET, SOCK_DGRAM, 0)) == -1) {
		tputs ("socket failed\n");
		return 1;
	}
	len = 1;
	/* Did the user include a password or kickme target? */
	if (addr != 0 && cmd[0] == 'k')
		len += sizeof (int32);

	if (key != NULLCHAR && cmd[0] != 'k')
		len += (int16) klen;

	if (route != NULLCHAR)
		len += (int16) (strlen (route) + 1);

	if (len == 1)
		data = &x;
	else
		data = mallocw ((size_t) len);

	fsock.sin_family = AF_INET;
	if ((fsock.sin_addr.s_addr = resolve (host)) == 0) {
		tprintf (Badhost, host);
		goto cleanup;
	}
	fsock.sin_port = port;

	switch (cmd[0]) {
		case 'r':
		case 'e':
			data[0] = (cmd[0] == 'r') ? SYS_RESET : SYS_EXIT;
			if (key != NULLCHAR)
				strncpy (&data[1], key, (unsigned) klen);	/*lint !e661 */
			break;
		case 'k':
			data[0] = KICK_ME;
			if (addr != 0)
				(void) put32 ((unsigned char *) &data[1], addr);	/*lint !e661 */
			break;
		case 'a':	/* add a route */
		case 'd':	/* drop a route */
			if (key == NULLCHAR || route == NULLCHAR)	{
				tprintf ("Command '%s' requires a password and a route entry!\n", cmd);
				goto cleanup;
			}
			data[0] = (cmd[0] == 'a') ? ROUTE_ADD : ROUTE_DROP;
			data[1] = (char) strlen(route);		/*lint !e661 */
			strcpy (&data[2], route);		/*lint !e661 !e662 */
			strncpy (&data[2 + strlen(route)], key, (unsigned) klen);	/*lint !e661 */
			break;
		default:
			tprintf ("Unknown command %s\n", cmd);
			goto cleanup;
	}

	/* Form the command packet and send it */
	if (sendto (s, data, len, 0, (char *) &fsock, sizeof (fsock)) == -1) {
		tprintf ("sendto failed: %s\n", sys_errlist[errno]);
		goto cleanup;
	}
cleanup:
	if (data != &x)
		free (data);
	close_s (s);
	return 0;
}



#if (defined(ALLCMD) || defined(ALLSESSIONS))
int
morecmd (int argc, char *argv[], void *p OPTIONAL)
{
struct session *sp = 0;
FILE *fp;
char buf[81];
char fname[256];
int row = 0;
int usesession = 0;

	/* Use a session if this comes from console - WG7J*/
	if (Curproc->input == Command->input) {
		usesession = 1;
		if ((sp = newsession (argv[1], MORE, 0)) == NULLSESSION) {
			return 1;
		}
		/* Put tty into raw mode so single-char responses will work */
		sp->ttystate.echo = sp->ttystate.edit = 0;
#ifdef UNIX
		row = Numrows - 1;
#else
		row = SCREENlength;
#endif
	}
	strncpy (fname, make_fname (Command->curdirs->dir, argv[1]), 256);
	if ((fp = fopen (fname, READ_TEXT)) == NULLFILE) {
		tprintf (NoRead, fname, sys_errlist[errno]);
		if (usesession) {
			(void) keywait (NULLCHAR, 1);
			freesession (sp);
		}
		return 1;
	}
	while ((void) fgets (buf, sizeof (buf), fp), !feof (fp)) {
		if ((argc < 3) || (strstr (buf, argv[2]) != NULLCHAR)) {
			tprintf ("%s", buf);
			if (usesession) {
				if (--row == 0) {
					row = keywait ("--More--", 0);
					switch (row) {
						case -1:
						case 'q':
						case 'Q':
						case 'n':
						case 'N':
							goto done;
#ifndef TNOS_68K
						case '\n':
#else
						case '\l':
#endif
						case '\r':
							row = 1;
							break;
						case ' ':
						default:
#ifdef UNIX
							row = Numrows - 1;
#else
							row = SCREENlength;
#endif
					}
				}
			}
		}
	}
done:	(void) fclose (fp);
	if (usesession) {
		(void) keywait (NULLCHAR, 1);
		freesession (sp);
	}
	return 0;
}



int
domore (int argc, char *argv[], void *p)
{
char **pargv;
int i;

	if (Curproc->input == Command->input) {
		/* Make private copy of argv and args,
		 * spawn off subprocess and return.
		 */
		pargv = (char **) callocw ((size_t) argc + 1, sizeof (char *));

		for (i = 0; i < argc; i++)
			pargv[i] = strdup (argv[i]);
		pargv[i] = NULL;
		(void) newproc ("more", 512, (void (*)(int, void *, void *)) morecmd, argc, (void *) pargv, p, 1);
	} else
		(void) morecmd (argc, argv, p);
	return 0;
}



int
dotail (int argc, char *argv[], void *p OPTIONAL)
{
register int handle, i;
register unsigned line = 0, rdsize = 2000;
off_t length;
char *buffer, fname[128];
register int numlines = 18;

	if (argc == 3) {
		numlines = atoi (argv[2]);
		if (!numlines)
			numlines = 18;
		else
			rdsize = (unsigned) (numlines * 100);
	}
	buffer = callocw (rdsize, sizeof (char));

	strncpy (fname, make_fname (Command->curdirs->dir, argv[1]), 128);
	if ((handle = open (fname, O_BINARY | O_RDONLY)) == -1) {	/* lint !e718 !e746 */
		tprintf (NoRead, fname, sys_errlist[errno]);
		free (buffer);
		return -1;
	}
	length = (long) filelength (handle);

	if ((unsigned) length > rdsize)
		length -= (long) rdsize;
	else {
		rdsize = (unsigned) length;
		length = 0;
	}

	lseek (handle, length, SEEK_SET);
	if (read (handle, buffer, rdsize) == -1) {
		tprintf (NoRead, fname, sys_errlist[errno]);
		close (handle);
		free (buffer);
		return -1;
	}
	for (i = (int) rdsize - 1; i > 0; i--) {
		if (buffer[i] == '\n')
			line++;
		if (line == (unsigned) numlines)
			break;
	}
	for (; (unsigned) i < rdsize; i++)
		tputc (uchar(buffer[i]));

	tputc ('\n');
	close (handle);
	free (buffer);
	return 0;
}

#endif /*ALLCMD*/


int
doman (int argc, char *argv[], void *p)
{
char *look;
char section = '0', old = '0';
char tmp[128];
char *pargv[2];

	if (argc == 3) {
		old = section = argv[1][0];
		look = argv[2];
	} else
		look = argv[1];

	if (section == '0') {
		for (section = '1'; section <= '9'; section++) {
			sprintf (tmp, "%s/cat%c/%s.%c", MANdir, section, look, section);
			if (!access (tmp, 0))
				break;
		}
	} else {
		sprintf (tmp, "%s/cat%c/%s.%c", MANdir, section, look, section);
		if (access (tmp, 0))
			section = '0';
	}

	if (section == '0' || section > '9') {
		tprintf ("No manual information found for '%s'", look);
		if (section == '0')
			tprintf (" in section %c", old);
		tputs ("\n");
	} else {
		pargv[1] = tmp;
		pargv[0] = argv[0];
		(void) domore (2, pargv, p);
	}
	return 0;
}



/* No-op command */
int
donothing (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return 0;
}



int
dosystime (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
time_t t;
struct tm *tmp;
long minutes;

	/* Get current time */
	(void) time (&t);
	tmp = localtime (&t);

	/* Print it */
	tprintf ("System time at %s (in %s): %s", Hostname,
#ifdef sun
		 tmp->tm_zone,
#else
#ifdef MSDOS
		 tmp->__tm_zone,
#else
		 tmp->tm_isdst ? tzname[1] : tzname[0],
#endif
#endif
		 asctime (tmp));
	if (argc > 1) {
#if defined(sun) || defined(__bsdi__) || defined(__FreeBSD__) || defined(MSDOS)
		minutes = tmp->tm_gmtoff / 60L;
#else
		minutes = (timezone - (tmp->tm_isdst * 3600L)) / -60L;
#endif
		tprintf ("   Local time is UTC%+ld minutes\n", minutes);
		tmp = gmtime (&t);
		tprintf ("   UTC time is %s", asctime (tmp));
	}
	return 0;
}



static int SendError = 1;

int
doerror (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&SendError, "Mail errors", argc, argv);
}



#ifdef BETA
int
docrashprot (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&CrashProtect, "Enable crash protection mode", argc, argv);
}
#endif



/* Mail a system message to the sysop - WG7J */
void
mail_error (const char *fmt,...)
{
FILE *txt;
va_list ap;
char *cp;
time_t t;

	if (!SendError)
		return;

	/* Create the data file */
	if ((txt = tmpfile ()) == NULL)
		return;

	/* Get current time */
	(void) time (&t);

	/* Print the text body */
	cp = ctime (&t);
	fprintf (txt, "On %s", cp);
	va_start (ap, fmt);		/*lint !e718 !e746 */
	(void) vfprintf (txt, fmt, ap);
	va_end (ap);
	fputc ('\n', txt);
	rewind (txt);
	(void) rdaemon (txt, NULLCHAR, NULLCHAR, "syserror", SysMessage, 'P', 0);

	(void) fclose (txt);

	/* Now kick the smtp server */
	smtptick (NULL);
}



/* Log messages of the form
 * Tue Jan 31 00:00:00 1987 44.64.0.7:1003 open FTP
 */
void
log (int s, const char *fmt,...)
{
va_list ap;
char *cp;
time_t t;
int i;
struct sockaddr fsocket;
char buf[SOBUF];
#ifdef	MSDOS
int fd;
#endif

	va_start (ap, fmt);		/*lint !e718 !e746 */
	(void) vsprintf (buf, fmt, ap);
	va_end (ap);
	statlog (buf);

	if (Logfp == NULLFILE)
		return;

	(void) time (&t);
	cp = TZTIME (&t);
	rip (cp);
	i = SOCKSIZE;
	fprintf (Logfp, "%s", cp);
	if (getpeername (s, (char *) &fsocket, &i) != -1)
		fprintf (Logfp, " %s", psocket (&fsocket));

	fprintf (Logfp, " - %s\n", buf);
	(void) fflush (Logfp);
#ifdef	MSDOS
	/* MS-DOS doesn't really flush files until they're closed */
	fd = fileno (Logfp);
	if ((fd = dup (fd)) != -1)
		close (fd);
#endif

}



/* Log messages of the form
 * Tue Jan 31 00:00:00 1987 44.64.0.7:1003 open FTP
 */
void
simple_log (int s, const char *str)
{
char *cp;
time_t t;
int i;
struct sockaddr fsocket;
#ifdef	MSDOS
int fd;
#endif

	statlog (str);

	if (Logfp == NULLFILE)
		return;

	(void) time (&t);
	cp = TZTIME (&t);
	rip (cp);
	i = SOCKSIZE;
	fputs (cp, Logfp);
	if (getpeername (s, (char *) &fsocket, &i) != -1)
		fprintf (Logfp, " %s", psocket (&fsocket));

	fputs (" - ", Logfp);
	fputs (str, Logfp);
	fputc ('\n', Logfp);
	(void) fflush (Logfp);
#ifdef	MSDOS
	/* MS-DOS doesn't really flush files until they're closed */
	fd = fileno (Logfp);
	if ((fd = dup (fd)) != -1)
		close (fd);
#endif

}



int
dosource (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
{
int linenum = 0;
char *inbuff, *intmp, *bptr;
FILE *fp;
char fname[128];

	/* Read command source file */
	strncpy (fname, make_fname (Command->curdirs->dir, argv[1]), 128);
	if ((fp = fopen (fname, READ_TEXT)) == NULLFILE) {
		tprintf (NoRead, fname, sys_errlist[errno]);
		return 1;
	}
	inbuff = (char *) mallocw (BUFSIZ);
	intmp = (char *) mallocw (BUFSIZ);
	while (fgets (inbuff, BUFSIZ, fp) != NULLCHAR) {
		strncpy (intmp, inbuff, BUFSIZ);
		linenum++;
		if (Verbose)
			tprintf ("%s", intmp);
		if (!Mprunning)
			Mprunning_err = 0;
		bptr = _variable_expansion (strdup (inbuff));
		if (cmdparse (Cmds, bptr, NULL) != 0) {
			tprintf ("*** file \"%s\", line %d: %s\n",
				 argv[1], linenum, intmp);
		}
		free (bptr);
		if (!Mprunning && Mprunning_err)
			tprintf (MPRUNNINGerr, intmp);
	}
#ifdef SCRIPTING
	if (Curproc->gotolabel) {
		free (Curproc->gotolabel);
		Curproc->gotolabel = NULLCHAR;
	}
#endif
	(void) fclose (fp);
	free (inbuff);
	free (intmp);
	Mprunning_err = 0;
	return 0;
}



/* if ThirdParty is not set - restrict the mailbox (S)end command to local only */
int
dothirdparty (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&ThirdParty, "Third-Party mail flag", argc, argv);
}



#if defined(ALLCMD) && !defined(UNIX)
int
domdump (int argc, char *argv[], void *p OPTIONAL)
{
unsigned int i;
char *addr;
unsigned int len = 8 * 16;	/* default is 8 lines of hex dump */

	if (argc < 2 || argc > 3) {
		tputs ("Usage:- dump <hex-address | .> [decimal-range] \n");
		return 0;
	}
	if (argv[1][0] == '.')
		addr = DumpAddr;/* Use last end address */
	else
		addr = (char *) htol (argv[1]);	/* get address of item being dumped */
	addr = (char *) ((((unsigned long) addr + 15) >> 4) << 4);	/* round up to modulo 16 */

	if (argc == 3) {
		len = atoi (argv[2]);
		len = ((len + 15) >> 4) << 4;	/* round up to modulo 16 */
	}
	if (len < 1 || len > 256) {
		tputs ("Invalid dump range. Valid is 1 to 256\n");
		return 0;
	}
	tprintf ("            Main Memory Dump Of Location %08lx\n", (long) addr);
	tputs ("Addr (offset)           Hexadecimal                         Ascii\n");
	tputs ("----                    -----------                         -----\n");

	for (i = 0; i < len; i += 16)
		fmtline ((uint32) (i + addr), (char *) (addr + i), 16);
	DumpAddr = (char *) (addr + i);	/* update address */
	return 0;
}



/* Print a buffer up to 16 bytes long in formatted hex with ascii
 * translation, e.g.,
 * 00000000: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?
 */
static void
fmtline (uint32 addr, char *buf, int16 len)
{
char line[81];
register char *aptr, *cptr;
register char c;

	memset (line, ' ', sizeof (line));
	ctohex (line, (int16) hibyte (addr >> 16));
	ctohex (line + 2, (int16) lobyte (addr >> 16));
	ctohex (line, (int16) hibyte (addr & 0x0000ffff));
	ctohex (line + 2, (int16) lobyte (addr & 0x0000ffff));
	aptr = &line[6];
	cptr = &line[55];
	while (len-- != 0) {
		c = *buf++;
		ctohex (aptr, (int16) uchar (c));
		aptr += 3;
		c &= 0x7f;
		if ((c > 0x1f) && (c < 0x7f))
			*cptr++ = c;
		else
			*cptr++ = '.';
	}
	*cptr++ = '\0';
	tprintf ("%s\n", line);
}



/* Convert byte to two ascii-hex characters */
static void
ctohex (register char *buf, register int16 c)
{
static char hex[] = "0123456789abcdef";

	*buf++ = hex[hinibble (c)];
	*buf = hex[lonibble (c)];
}

#endif

extern void conversWrite (char *str, char *user);



int
dowrite (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
{
int i, s;
struct mbx *m = NULLMBX;

#ifdef CONVERS
	conversWrite (argv[2], argv[1]);
#endif
	if ((s = atoi (argv[1])) == 0) {	/* must be a name */
#ifdef MAILBOX
		/* check the mailbox users */
		for (i = 0; i < NUMMBX; i++) {
			if ((m = Mbox[i]) != NULLMBX) {
				if (!strcmp (m->name, argv[1]))
					break;
			}
		}
		if (i == NUMMBX)
			return 0;
		if (m == NULLMBX)
			return 0;
		s = m->user;
#else
		return 0;
#endif
	} else
		return 0;
	usprintf (s, FromSysop, argv[2]);
	usflush (s);

	return 0;
}



int
doshutdown (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
{
int32 delayval = 0;
char *str = NULLCHAR;
char buffer[1024];

	str = argv[2];

	if (strcasecmp (argv[1], "now"))
		delayval = atol (argv[1]);

	if (delayval)	{
		sprintf (buffer, "System being shutdown in %ld seconds%s%s",
			delayval, (str != NULLCHAR) ? "\n*** " : "",
			(str != NULLCHAR) ? str : "");
		tcmdprintf (FromSysop, buffer);
		(void) _dowriteall (buffer);
		kpause (delayval * 1000L);
	}

	sprintf (buffer, "System now being shutdown%s%s",
		(str != NULLCHAR) ? "\n*** " : "",
		(str != NULLCHAR) ? str : "");
	tcmdprintf ("\n");
	tcmdprintf (FromSysop, buffer);
	(void) _dowriteall (buffer);
	kpause (5000L);
	where_outta_here (0, "doshutdown");
	return 0;			/*lint !e527 */
}



#ifdef MAILBOX
extern void conversWriteall (char *str);


static int
_dowriteall (char *str)
{
register struct mbx *m;
int i;

	for (i = 0; i < NUMMBX; i++)
		if ((m = Mbox[i]) != NULLMBX) {
			usprintf (m->user, FromSysop, str);
			usflush (m->user);
		}
#ifdef CONVERS
	conversWriteall (str);
#endif
	return 0;
}



/* write a message to all nodeshell users
 * argv[1] is the message.
 */
int
dowriteall (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
{
	return _dowriteall (argv[1]);
}

#endif



int
dostatus (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
time_t nowtime, elapsedtime;
unsigned int days, hrs, mins, secs;
char buf[80];

	nowtime = time (&nowtime);	/* current time */
	elapsedtime = nowtime - StartTime;	/* nos elapsed time */

	tprintf (Nosversion, Version, Version2);
	tputs (Compiled);
	if (strlen (symname) > 9) {
		strncpy (buf, &symname[7], 80);
		buf[strlen (buf) - 2] = 0;
		tprintf ("Symbolic RCS Name for this Executable: '%s'\n", buf);
	}
#ifdef MSDOS
	tprintf ("Running under %s - Version %d.%d\n", _os_flavor, _osmajor, _osminor);
#endif
	tprintf ("Tty: %d rows, %d columns\n",
#ifdef UNIX
		 Numrows, Numcols);
#else
		 (int) SCREENlength, (int) SCREENwidth);
#endif

	tprintf ("\nThe system time is %s", ctime (&nowtime));
	tprintf ("TNOS was started on %s\n", ctime (&StartTime));
	secs = (unsigned int) ((long) elapsedtime % 60);
	elapsedtime = elapsedtime / 60;
	mins = (unsigned int) ((long) elapsedtime % 60);
	elapsedtime = elapsedtime / 60;
	hrs = (unsigned int) ((long) elapsedtime % 24);
	elapsedtime = elapsedtime / 24;
	days = (unsigned int) (long) elapsedtime;
	tprintf ("Elapsed time => %u days:%02u hours:%02u minutes:%02u seconds.\n\n", days, hrs, mins, secs);
	tprintf ("Sessions = %-d, Sockets = %-d, PBBS processes = %-d", Nsessions, Nusock, NUMMBX);
	tprintf (", Serial port devices = %-d\n", ASY_MAX);
	tprintf ("AX25/SLIP devices = %-d", SLIP_MAX);
#ifdef AXIP
	tprintf (", AXIP interfaces = %-d", AXIPlinks);
#endif
#ifdef AXUDP
	tprintf (", AXUDP interfaces = %-d", AXUDPlinks);
#endif
#ifdef NETROM
	tprintf (", NETROM circuits = %-d", NR4MAXCIRC);
#endif
	tputs ("\n");
#ifdef UNIX
	tprintf ("Location of the LOCKDIR: %s\n", LOCKDIR);
#endif
	tputs ("\n");
#ifdef ALLCMD
	tprintf ("The station is currently %sttended.\n", Attended ? "A" : "Una");
	tputs ("The 'Message Of The Day' is ");
	if (Motd != NULLCHAR)
		tprintf ("\n%s", Motd);
	else
		tputs ("not set!\n");
#endif
	tputc ('\n');
	return 0;
}



#ifdef ALLCMD
int
domotd (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc > 2) {
		tputs ("Usage: motd \"<your message>\"\n");
		return 1;
	}
	if (argc < 2) {
		if (Motd != NULLCHAR)
			tprintf ("%s", Motd);
	} else {
		if (Motd != NULLCHAR) {
			free (Motd);
			Motd = NULLCHAR;	/* reset the pointer */
		}
		if (!strlen (argv[1]))
			return 0;	/* clearing the buffer */

		Motd = mallocw (strlen (argv[1]) + 2);	/* allow for the EOL char etc */
		strcpy (Motd, argv[1]);
		strcat (Motd, "\n");	/* add the EOL char */
	}
	return 0;
}

#endif /*ALLCMD*/


/* Command history, see also pc.c - WG7J */
static void
logcmd (char *cmd)
{
struct hist *new;
char *cp;

	if (!Maxhistory)	/* don't keep history */
		return;

	/* Get rid of \n; this is also done in cmdparse().
	 * We HAVE to do this here, since the string is NOT null-terminated when
	 * it comes from recv_mbuf()  !!!! rip() makes it nullterminated.
	 */
	rip (cmd);
	cp = cmd;
	while (*cp == ' ' || *cp == '\t')
		cp++;
	if (!*cp)		/* Empty command */
		return;

	if (Histrysize < Maxhistory) {	/* Add new one */
		Histrysize++;
		if (!Histry) {	/* Empty list */
			/* Initialize circular linked list */
			Histry = mallocw (sizeof (struct hist));

			Histry->next = Histry->prev = Histry;
		} else {
			new = mallocw (sizeof (struct hist));

			/* Now link it in */
			Histry->next->prev = new;
			new->next = Histry->next;
			new->prev = Histry;
			Histry->next = new;
			Histry = new;
		}
	} else {
		/* Maximum number stored already, use the oldest entry */
		Histry = Histry->next;
		free (Histry->cmd);
	}
	Histry->cmd = strdup (cp);
}



static void
freehistory (void)
{
struct hist *h, *last;

	if ((h = Histry) == NULL)
		return;
	do {
		free (h->cmd);
		last = h;
		h = h->prev;
		free (last);
	} while (h != Histry);
	Histry = NULL;
	Histrysize = 0;
}



int
dohistory (int argc, char *argv[], void *p OPTIONAL)
{
struct hist *h;
int num;

	if (argc > 1) {
		Maxhistory = atoi (argv[1]);
		freehistory ();
		return 0;
	}
	tprintf ("Max recall %d\n", Maxhistory);
	if ((h = Histry) == NULL)
		return 0;
	num = 0;
	do {
		tprintf ("%.2d: %s\n", num++, h->cmd);
		h = h->prev;
	} while (h != Histry);
	return 0;
}



#if 0
#ifndef UNIX

/* This adds some additional checks to the fopen()
 * in the Borland C++ Run Time Library.
 * It fixes problem with users trying to open system devices like
 * CON, AUX etc and hang a system.
 * WG7J, 930205
 */
#undef fopen
FILE *fopen (const char * __path, const char * __mode);

static char *InvalidName[] =
{
	"NUL",
	"CON",
	"AUX",
	"PRN",
	"LPT1",
	"LPT2",
	"LPT3",
	"COM1",
	"COM2",
	"COM3",
	"COM4",
	"MOUSE$",
	"CLOCK$",
	NULLCHAR,
};


FILE *
newfopen (const char *filename, const char *type)
{
int i;

	for (i = 0; InvalidName[i] != NULLCHAR; i++)
		if (stricmp (InvalidName[i], filename) == 0)
			return NULL;

	return fopen (filename, type);
}

#endif /* UNIX */
#endif



/* Repeat a command - taken from 930104 KA9Q NOS
   WA3DSP 1/93
*/
int
dorepeat (int argc, char *argv[], void *p)
{
int32 interval;
int ret;
struct session *sp;

	if (isdigit (argv[1][0])) {
		interval = atol (argv[1]);
		argc--;
		argv++;
	} else {
		interval = MSPTICK;
	}
	if ((sp = newsession (argv[1], REPEAT, 0)) == NULLSESSION) {
		tputs ("Too many sessions\n");
		return 1;
	}
	_setcursortype (_NOCURSOR);
	while (sp == Current) {
		clrscr();
		/* gotoxy seems to work better (but doen't clear screen - turn cursor off?? */
		/* gotoxy (1, 1); */
		ret = subcmd (Cmds, argc, argv, p);
		if (ret != 0 || kpause (interval) == -1)
			break;
	}
	_setcursortype (_NORMALCURSOR);
	freesession (sp);
	return 0;
}



int 
DisplayFile (const char *fname, int thesocket)
{
FILE *fp;
int size = 0;

	if ((fp = fopen (fname, READ_TEXT)) != (FILE *) 0) {
		(void) sendfile (fp, thesocket, ASCII_TYPE, 0);
		size = ftell (fp);
		(void) fclose (fp);
	}
	return (size);
}



#ifdef SETPSINFO
void
setprocname (buf)
char *buf;
{
register char *p, *bp, ch;
register int i;

	/* make ps print our process name */
	p = origargv[0];
	*p++ = '-';

	i = (int) strlen(buf);
	if (i > LastArgv - p - 2) {
		i = LastArgv - p - 2;
		buf[i] = '\0';
	}
	bp = buf;
	while ((ch = *bp++) != 0)
		if (ch != '\n' && ch != '\r')
			*p++ = ch;
	while (p < LastArgv)
		*p++ = ' ';
}
#endif



void
crash_it_already (char const *where)
{
	/* TRY a log */
	log (-1, "A 'crashprotect'-preventable restart occurred from a %s", where);
	exit (1);
}


int
shall_we_crash (void)
{
	return (CrashProtect == 0);
}
