/* Main-level network program:
 * initialization
 * keyboard processing
 * generic user commands
 *
 * Copyright 1991 Phil Karn, KA9Q
 * Mods by KO4KS
 */
#include <stdio.h>
#include <time.h>
#if	defined(__TURBOC__) && defined(MSDOS)
#include <fcntl.h>
#include <dos.h>
#include <io.h>
#include <conio.h>
#include <ctype.h>
#include <dir.h>
#endif
#ifdef UNIX
#include <fcntl.h>
#include <sys/stat.h>
#endif
#include "global.h"
#include "config.h"
#ifdef	ANSIPROTO
#include <stdarg.h>
#endif
#include "mbuf.h"
#include "timer.h"
#include "proc.h"
#include "iface.h"
#include "ip.h"
#include "tcp.h"
#include "udp.h"
#include "ax25.h"
#include "kiss.h"
#ifdef ETHER
#include "enet.h"
#endif
#include "netrom.h"
#include "ftpcli.h"
#include "telnet.h"
#include "tty.h"
#include "session.h"
#include "hardware.h"
#include "usock.h"
#include "socket.h"
#ifdef LZW
#include "lzw.h"
#endif
#include "cmdparse.h"
#include "commands.h"
#include "daemon.h"
#include "devparam.h"
#include "domain.h"
#include "files.h"
#include "main.h"
#include "mailbox.h"
#include "remote.h"
#include "trace.h"
#ifdef fprintf
#undef fprintf
#endif
#include "mailutil.h"
#include "smtp.h"

#ifdef XSPAWN
#include "xspawn.h"
#endif

extern struct cmds DFAR Cmds[],DFAR Startcmds[],DFAR Stopcmds[],Attab[];
extern int SYSback, SYSfore;
extern char vgaDesired;
extern unsigned char SCREENlength, SCREENwidth;

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

#ifdef __TURBOC__
int dofstat __ARGS((void));
#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 DFAR Tnosversion[];
char Nosversion[] = "KA9Q NOS version %s\n";
#ifdef MSDOS
char NosLoadInfo[] = "TNOS load info: CS=0x%04x DS=0x%04x";
#endif
char Prompt[] = "Net";
char Noperm[] = "Permission denied.\n";
char Nosock[] = "Can't create socket\n";
char FromSysop[] = "*** Msg from SYSOP: %s\n";
char SysMessage[] = "System message";
char *Hostname = NULLCHAR;
char *Motd = NULLCHAR;			/* Message Of The Day */
int Attended = TRUE;            /* default to attended mode */
int ThirdParty = TRUE;			/* Allows 3rd party mail by default */
int main_exit = FALSE;			/* from main program (flag) */
int Mprunning = 0;		/* flag for other parts (domain) to signal
                         * that we are fully configured running.
                         */
struct proc *Cmdpp;
struct proc *Display;
#ifdef	LZW
int Lzwmode = LZWCOMPACT;
int16 Lzwbits = LZWBITS;
#endif

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

static char PromptType;

static char *DumpAddr = NULL;		/* Memory dump pointer */
static 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 */
int Histrysize;
int Maxhistory = 10;

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

#ifdef UNIX
static char **origargv;
#endif


int chksession __ARGS((struct session *sp));
extern void usercvt __ARGS((void));
extern void setscreens __ARGS((int back, int fore, int clr));
static void ctohex __ARGS((char *buf,int16 c));
static void fmtline __ARGS((int16 addr,char *buf,int16 len));
#ifdef MSDOS
extern void VMSinit (), VMSterm();
#endif
extern void displayStatLine __ARGS((int offset));
extern void statLineToggle __ARGS((void));
extern void statlog __ARGS((char *buf));
extern void assign_filenames __ARGS((char *, int));
static void logcmd __ARGS((char *));
static void freehistory __ARGS((void));

#ifdef MSDOS
char cpuid[]={
 0x52,0x33,0xD2,0x9C,0x58,0x80,0xE4,0x0F,0x50,0x9D,0x9C,
 0x58,0x80,0xE4,0xF0,0x80,0xFC,0xF0,0x74,0x12,0xFE,0xC2,
 0x9C,0x58,0x80,0xCC,0xF0,0x50,0x9D,0x9C,0x58,0x80,0xE4,
 0xF0,0x74,0x02,0xFE,0xC2,0xFE,0xC2,0x8B,0xC2,0x5A,0xCB},
 *cpuname[]={"8088/8086/V20/V30","80286","80386/80486"};
#endif


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

int
#ifdef PROTOTYPES
main(int argc, char **argv)
#else
main(argc,argv)
int argc;
char *argv[];
#endif
{
	char *inbuf,*intmp;
	FILE *fp;
	struct daemon *tp;
	struct mbuf *bp;
	int i;
	int c;
	int did_init;
#ifdef UNIX
	int no_itimer;
	char *trace_sm = 0;
	char *def_sm = 0;
	static int oops;
#else
    struct text_info ti;
#endif
	struct cur_dirs dirs;
#ifdef MSDOS
	int cpu,(DFAR *CpuId)()=(int (DFAR *)())cpuid;
#endif

#ifdef UNIX
    if (oops++)
    {
	iostop();
	fprintf(stderr, "NOS PANIC: NOS main re-entered.\n");
	fflush(stderr);
	fflush(stdout);
	kill(getpid(), 11);
    }
    origargv = argv;
#endif
#ifdef XSPAWN
        /* These vectors WILL be intercepted by NOS in COMMAND.ASM
           Go ahead and tell XSPAWN to save their current state, now.
           Vector 0x23 is also used by NOS, but is ALREADY being preserved
           by the XSPAWN routines	*/
	addvect (8, CURRENT);
	addvect (0x10, CURRENT);
	addvect (0x13, CURRENT);
	addvect (0x1b, CURRENT);
	addvect (0x21, CURRENT);
	addvect (0x24, CURRENT);
	addvect (0x28, CURRENT);
#endif
	StartTime = time(&StartTime);			/* NOS Start_Up time */
#ifdef UNIX
    SRANDOM((getpid() << 16) ^ time((long *) 0));
#else
	randomize();
#endif

#ifdef UNIX
	no_itimer = 0;
#else
    /* 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
     */
    Numrows = (int) *(char far *)MK_FP(0x40,0x84) + 1;
    gettextinfo(&ti);
    Numcols = ti.screenwidth;
/*
    Numrows = ti.screenheight;
*/

    if(Numrows == 1)
	       Numrows = 25;
#endif
    did_init = 0;
#ifdef UNIX
    while((c = getopt(argc,argv,"s:d:f:o:nbvDS:T:")) != EOF){
#else
    while((c = getopt(argc,argv,"k:g:s:f:x:d:o:nbvec")) != EOF){
#endif
		switch(c){

#ifdef TRACE
		case 'n':	/* No session for tracing */
			Tracesession = 0;
			break;
#endif
#ifndef UNIX
		case 'k':	/* background color */
			i= (getcolor (optarg) + 10);
			if (i)
				SYSback = i;
			break;
		case 'g':	/* foreground color */
			i = getcolor (optarg);
			if (i)
				SYSfore = i;
			break;
#endif
#ifdef TNOS_68K
		case 'c':	/* curses mode */
			UseCurses = 1;
			break;
#endif
	        case 's':   /* Number of sockets */
			Nusock = atoi(optarg);
			break;
		case 'f':   /* read a filename config file */
			assign_filenames(optarg, 1);
			did_init = 1;
			break;
#ifdef XSPAWN
		case 'x':   /* Path for XSWAP temp file */
			_swappath = strdup(optarg);
			break;
#endif
		case 'd':	/* Root directory for various files */
			initroot(optarg);
			break;
#ifdef	__TURBOC__
		case 'b':	/* Use BIOS for screen output */
			directvideo = 0;
			break;
#endif
		case 'v':
			Verbose = 1;
			break;
#ifdef MSDOS
		case 'e':	/* EGA/VGA 43/50 mode desired */
			vgaDesired = 1;
			break;
#endif
		case 'o':	/* override number of interactive sessions */
			Nsessions = atoi (optarg);
			if (Nsessions < 5)
				Nsessions = 5;	/* but not less than 5 */
			break;
#ifdef UNIX
	case 'D':
	    no_itimer = 1;	/* for debugging */
	    break;
	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
		}
	}
#if defined(MSDOS)
	VMSinit ();
#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
	ioinit();
#endif
#ifdef MAILBOX
	{
	char tmpbuf[128];
	sprintf (tmpbuf, "%s/control", Mailspool);
#ifdef UNIX
	mkdir(tmpbuf, 0777);
#else
	mkdir (tmpbuf);
#endif
	sprintf (tmpbuf, "%s/users", Mailspool);
#ifdef UNIX
	mkdir(tmpbuf, 0777);
#else
	mkdir (tmpbuf);
#endif
	}
#endif
	sockinit();
	Cmdpp = mainproc("cmdintrp");

	Sessions = (struct session *)callocw(Nsessions,sizeof(struct session));
	for (i = 0; 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
#ifndef TNOS_68K
    Display = newproc("display",250,display,0,NULLCHAR,NULL,0);
#else
    Display = newproc("display",512,display,0,NULLCHAR,NULL,0);
#endif
#endif
    setscreens (0, 0, 1);
    if (firstMsg)	{
    	tputs (firstMsg);
    	free (firstMsg);
    }
    tprintf(Tnosversion);
    tputs(Version2);
    tputs("Copyright 1991 by Phil Karn (KA9Q) and contributors.\n");
#ifdef MSDOS
    cpu=(*CpuId)();
    tprintf("Processor detected: %s\n",cpuname[cpu-1]);
#endif
	rflush();

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

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

	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')
                    	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){
		inbuf = mallocw(BUFSIZ);
		intmp = mallocw(BUFSIZ);
		while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){
			strcpy(intmp,inbuf);
			if(Verbose){
				tprintf("%s",intmp);
				rflush();
			}
			if(cmdparse(Cmds,inbuf,NULL) != 0)	{
				tprintf("input line: %s",intmp);
				rflush();
			}
		}
		fclose(fp);
		free(inbuf);
		free(intmp);
	}

	Mprunning = 1;	/* we are on speed now */
	Command->flowmode = 1;		/* set 'more' paging on command screen */

#ifdef UNIX
	SCREENlength = Numrows;
	SCREENwidth = Numcols;
#endif

	/* Now loop forever, processing commands */
	for(;;){
		netPrompt ();
		if(recv_mbuf(Command->input,&bp,0,NULLCHAR,0) != -1){
			logcmd(bp->data);
			(void)cmdparse(Cmds,bp->data,Lastcurr);
			free_p(bp);

		}
	}
}


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


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

	/* Keyboard process loop */
	for(;;){
		c = kbread();
#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);
/*			displayStatLine (0L); */
			/* set 'more' paging on command screen */
			Command->flowmode = 1;
		}
        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); */
						Lastcurr = Current;
						Current = Trace;
						swapscreen(Lastcurr,Current);
						/* turn off 'more' paging on trace screen */
						Trace->flowmode = 0;
					} else {
						/* Toggle back to previous session */
/*						swapscreen(Trace,Lastcurr); */
						Current = Lastcurr;
						Lastcurr = Trace;
						swapscreen(Lastcurr,Current);
					}
				}
				break;
#endif
			case -105:	/* INSERT pressed, statline toggle */
				statLineToggle ();
				break;
			case -106:	/* DEL pressed, toggle flow control */
				Current->flowmode ^= 1;
				break;
			case -107:	/* HOME pressed, kick current session */
				dokick (0, 0, Current);
				break;
			case -108:	/* END pressed, end current session */
				doreset (0, 0, Current);
				break;
			case -109:	/* PGUP pressed, goto previous session */
				j = (Current->index - 1) ? Current->index - 1 : 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);
		psignal(&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){
				send_mbuf(Current->input,bp,0,NULLCHAR,0);
			}
		}
	}
}

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

/*Lock the keyboard*/
int
dolock(argc,argv,p)
int argc;
char *argv[];
void *p;
{

	extern char Noperm[];

	/*allow only keyboard users to access the lock command*/
	if(Curproc->input != Command->input) {
		tputs(Noperm);
		return 0;
	}
	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;
}

#endif

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

	if(argc == 1) {
		switch (PromptType)	{
			case 2:		str = "Dirname";
					break;
			case 1:		str = "Hostname";
					break;
			default:	str = "Normal";
		}
		tprintf ("Prompt: %s\n", str);
	        return 0;
	}
        if(*argv[1] == 'h')	/* use the hostname */
        	PromptType = 1;
        else if (*argv[1] == 'd')	/* use the dirname */
        	PromptType = 2;
        else if (*argv[1] == 'n')	/* use the normal prompt */
        	PromptType = 0;
        else
		tputs("Usage: prompt [hostname | dirname | normal]\n");
	return 0;
}

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

	for(i=1;i < argc; i++)	{
		strcpy(fname,make_fname(Command->curdirs->dir,argv[i]));
#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);
			recvline (Curproc->input, filenm, 13);
			if (toupper (*filenm) != 'Y')
				continue;
			cp = (char *) 1;
		}
#endif
		filedir(fname, 0, filenm);
		if ((cp = strrchr (fname, '/')) != NULLCHAR)
			*cp = 0;
		for ( ; *filenm; 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(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char fname1[128];
char fname2[128];

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


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

	if (argc > 2)	{
		strcpy(fname2,make_fname(Command->curdirs->dir,argv[2]));
#ifdef DOS_GETFILEATTR
		_dos_getfileattr (fname2, &attr);
		if(attr & FA_DIREC)	{
			usedir = 1;
			dirnam = fname2; 
		}
#endif
	} else
		usedir = 1;
	strcpy(fname,make_fname(Command->curdirs->dir,argv[1]));
#ifdef DOS_GETFILEATTR
	if (!_dos_getfileattr (fname, &attr) && (attr & FA_DIREC))	{
		strcat (fname, "/*.*");
		cp = (char *) 1;
	}
#endif
	filedir (fname, 0, filenm);
	if ((cp = strrchr (fname, '/')) != NULLCHAR)
		*cp = 0;
	for ( ; *filenm; 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]);
			fclose (old);
			continue;
		}
		strlwr (fullname);
		tprintf ("Copying '%s'...\n", fullname);
		/* Now go copy */
		count = 0;
		while((ch = fgetc(old)) != EOF) {
			fputc(ch,new);
			if(!(++count))      /* be polite to other users */
				pwait(NULL);
		}
		fclose(old);
		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)
int resetme;
{
    int i;
    time_t StopTime;
    FILE *fp;
    char *inbuf,*intmp;

    if (NextCmd)
    	NextCmd = 1;
/*    inbuf = NextCmdMsg;
      NextCmdMsg = NULLCHAR;
      free (inbuf);		*/

    /* Execute sequence of commands taken from file "~/onexit.nos" */
    /* From iw0cnb */
    if((fp = fopen(Onexit,READ_TEXT)) != NULLFILE){
		inbuf = mallocw(BUFSIZ);
		intmp = mallocw(BUFSIZ);
		while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){
			strcpy(intmp,inbuf);
            if(Verbose){
				tprintf("%s",intmp);
				rflush();
            }
            if(cmdparse(Cmds,inbuf,NULL) != 0){
				tprintf("input line: %s",intmp);
			}
		}
		fclose(fp);
		free(inbuf);
		free(intmp);
	}
	StopTime = time(&StopTime);
	main_exit = TRUE;	/* let everyone know we're out of here */
	reset_all();
	if(Dfile_updater != NULLPROC)
		alert(Dfile_updater,0);	/* don't wait for timeout */
	for(i=0;i<100;i++)
		pwait(NULL);	/* Allow tasks to complete */
#ifdef TRACE
    shuttrace();
#endif
#if defined(MSDOS)
    VMSterm ();
#endif
    log(-1,"TNOS was stopped");
	if(Logfp){
		fclose(Logfp);
		Logfp = NULLFILE;
	}
#ifdef UNIX
    detach_all_asy();		/* make sure everything is unlocked */
    pwait(NULL);
#endif
    iostop();
    if(resetme)
#ifdef UNIX
    {
	if (fork() == 0)
	    abort();
	execvp(origargv[0], origargv); /* re-run NOS */
    }
#else
        sysreset();
#endif
    freehistory ();
    free_dirs(Command->curdirs);         
#ifdef TNOS_68K
    alm_delete (TNOS_68KAlarm);
#endif
    exit(0);
}

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

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

	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 ();
		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 = elapsedtime % 60;
	elapsedtime = elapsedtime / 60;
	mins = elapsedtime % 60;
	elapsedtime = elapsedtime / 60;
	hrs = elapsedtime % 24;
	elapsedtime = elapsedtime / 24;
	days = elapsedtime;
	tprintf("TNOS Exiting - Runtime => %u days:%02u hours:%02u minutes:%02u seconds.\n",days,hrs,mins,secs);
	tflush();
	mspause(1500);
	where_outta_here(0); /*No reset!*/
	return 0;   /* To satisfy lint */
}

extern char Chostname[], CConsole[];
#define CNAMELEN 16

int
dohostname(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *cp;

	if(argc < 2)
		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){
				tprintf("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
	return 0;
}

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

	if(argc < 2){
		if(Logfp)
			tprintf("Logging to %s\n",logname);
		else
			tprintf("Logging off\n");
		return 0;
	}
	if(Logfp){
		log(-1,"TNOS log closed");
		fclose(Logfp);
		Logfp = NULLFILE;
		free(logname);
		logname = NULLCHAR;
	}
	if(strcmp(argv[1],"stop") && strcmp(argv[1], "off")){
		strcpy(fname,make_fname(Command->curdirs->dir,argv[1]));
		logname = strdup(fname);
		Logfp = fopen(logname,APPEND_TEXT);
		log(-1,"TNOS v%s was started at %s", Version, ctime(&StartTime));
#ifdef	MSDOS
        log(-1,NosLoadInfo, _CS, _DS);
#endif
	}
	return 0;
}


/* Attach an interface
 * Syntax: attach <hw type> <I/O address> <vector> <mode> <label> <bufsize> [<speed>]
 */
int
doattach(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return subcmd(Attab,argc,argv,p);
}
/* Manipulate I/O device parameters */
int
doparam(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	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){
		tprintf("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(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	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(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2)
		tprintf("0x%x\n",Escape);
	else 
		Escape = *argv[1];
	return 0;
}
#endif	MSDOS
/* Generate system command packet. Synopsis:
 * remote [-p port#] [-k key] [-a hostname] <hostname> reset|exit|kickme
 */
int
doremote(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct sockaddr_in fsock;
	int s,c;
	char *data,x;
	int16 port,len;
	char *key = NULLCHAR;
	int klen;
	int32 addr = 0;
	char *cmd,*host;

	port = IPPORT_REMOTE;	/* Set default */
	optind = 1;		/* reinit getopt() */
#ifdef __GNUC__
	klen = 0;		/* defeat spurious "uninitialized" warning */
#endif
	while((c = getopt(argc,argv,"a:p:k:s:")) != EOF){
		switch(c){
		case 'a':
			if((addr = resolve(optarg)) == 0){
				tprintf(Badhost,optarg);
				return -1;
			}
			break;
		case 'p':
			port = atoi(optarg);
			break;
		case 'k':
			key = optarg;
			klen = strlen(key);
			break;
		case 's':
			Rempass = strdup(optarg);
			return 0;	/* Only set local password */
		}
	}
	if(optind > argc - 2){
		tprintf("Insufficient args\n");
		return -1;
	}
	host = argv[optind];
	cmd = argv[optind + 1];
	if((s = socket(AF_INET,SOCK_DGRAM,0)) == -1){
		tprintf("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] == 'r' || cmd[0] == 'e')) 
		len += klen;

	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':
		data[0] = SYS_RESET;
		if(key != NULLCHAR)
			strncpy(&data[1],key,(unsigned)klen);
		break;
	case 'e':
		data[0] = SYS_EXIT;
		if(key != NULLCHAR)
			strncpy(&data[1],key,(unsigned)klen);
		break;
	case 'k':
		data[0] = KICK_ME;
		if(addr != 0)
			put32(&data[1],addr);
		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(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct session *sp;
	FILE *fp;
    char buf[81];
    char fname[256];
	int row;

    int usesession = 0;

    /* Use a session if this comes from console - WG7J*/
#ifdef __GNUC__
	row = 0;		/* defeat uninitialized warnings */
	sp = 0;
#endif
    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
    }

    strcpy(fname,make_fname(Command->curdirs->dir,argv[1]));
    if((fp = fopen(fname,READ_TEXT)) == NULLFILE){
        tprintf(NoRead,fname,sys_errlist[errno]);
        if(usesession) {
            keywait(NULLCHAR,1);
            freesession(sp);
        }
        return 1;
	}
	while(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:	fclose(fp);
    if(usesession) {
        keywait(NULLCHAR,1);
        freesession(sp);
    }
    return 0;
}

int
domore(argc,argv,p)
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;
        newproc("more",512,(void (*)__ARGS((int,void *,void *)))morecmd,argc,(void *)pargv,p,1);
    } else
        morecmd(argc,argv,p);
    return 0;
}



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

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

	strcpy(fname,make_fname(Command->curdirs->dir,argv[1]));
	if ((handle = open (fname, O_BINARY | O_RDONLY)) == -1) {
        tprintf(NoRead,fname,sys_errlist[errno]);
		free(buffer);
		return -1;
	}
	length = filelength (handle);

	if (length > rdsize) {
		length -= rdsize;
	} else {
		rdsize = (int) 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 = rdsize - 1; i > 0; i--) {
		if (buffer[i] == '\n')
			line++;
		if (line == numlines)
			break;
	}
	for (; i < rdsize; i++)
		tputc(buffer[i]);

	tprintf("\n");
	close(handle);
	free(buffer);
	return 0;
}
#endif /*ALLCMD*/

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

int dosystime(argc,argv,p)
int argc;
char *argv[];
void *p;
{
long t;

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

    /* Print it */
    tprintf("System time at %s: %s",Hostname, ctime(&t));
    return 0;
}


static int SendError = 1;

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

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

    if(!SendError)
	return;

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

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

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

    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
 */
#if	defined(ANSIPROTO)
void
log(int s,char *fmt, ...)
{
va_list ap;
char *cp;
long t;
int i;
struct sockaddr fsocket;
#ifdef	MSDOS
int fd;
#endif
char buf[80];

	va_start(ap,fmt);
	vsprintf(buf,fmt,ap);
	va_end(ap);
	statlog (buf);

	if(Logfp == NULLFILE)
		return;

	time(&t);
	cp = ctime(&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);
	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

}
#else
/*VARARGS2*/
void
log(s,fmt,arg1,arg2,arg3,arg4,arg5)
int s;
char *fmt;
int arg1,arg2,arg3,arg4,arg5;
{
char *cp;
long t;
int fd,i;
struct sockaddr fsocket;
char buf[80];

	sprintf(buf,fmt,arg1,arg2,arg3,arg4,arg5);
	statlog (buf);

	if(Logfp == NULLFILE)
		return;
	time(&t);
	cp = ctime(&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);
	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
}
#endif

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

	/* Read command source file */
	strcpy(fname,make_fname(Command->curdirs->dir,argv[1]));
	if((fp = fopen(fname,READ_TEXT)) == NULLFILE){
		tprintf(NoRead,fname,sys_errlist[errno]);
		return 1;
	}

	inbuf = malloc(BUFSIZ);
	intmp = malloc(BUFSIZ);
	while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){
		strcpy(intmp,inbuf);
		linenum++;
		if(Verbose)
			tprintf("%s",intmp);
		if(cmdparse(Cmds,inbuf,NULL) != 0){
			tprintf("*** file \"%s\", line %d: %s\n",
				argv[1],linenum,intmp);
		}
	}
	fclose(fp);
	free(inbuf);
	free(intmp);
	return 0;
}

/* if unattended mode is set - restrict ax25, telnet and maybe other sessions */
int
doattended(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setbool(&Attended,"Attended flag",argc,argv);
}

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

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

	if(argc < 2 || argc > 3) {
		tprintf("Usage:- dump <hex-address | .> [decimal-range] \n");
		return 0;
	}
	if(argv[1][0] == '.')
		addr = DumpAddr;		/* Use last end address */
	else
		addr = ltop(htol(argv[1]));	/* get address of item being dumped */

	if(argc == 3) {
		len = atoi(argv[2]);
		len = ((len + 15) >> 4) << 4;	/* round up to modulo 16 */
	}

	if(len < 1 || len > 256) {
		tprintf("Invalid dump range. Valid is 1 to 256\n");
		return 0;
	}
#if !defined(TNOS_68K) && !defined(UNIX)
	tprintf("            Main Memory Dump Of Location %Fp\n", addr);
#else
	tprintf("            Main Memory Dump Of Location %08lx\n", (long)addr);
#endif
	tprintf("Addr (offset)           Hexadecimal                         Ascii\n");
	tprintf("----                    -----------                         -----\n");

	for(i = 0; i < len; i += 16)
		fmtline((int16)i, (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.,
 * 0000: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?
 */
static void
fmtline(addr,buf,len)
int16 addr;
char *buf;
int16 len;
{
	char line[81];
	register char *aptr,*cptr;
	register char c;

	memset(line,' ',sizeof(line));
	ctohex(line,(int16)hibyte(addr));
	ctohex(line+2,(int16)lobyte(addr));
	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(buf,c)
register char *buf;
register int16 c;
{
	static char hex[] = "0123456789abcdef";

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

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

    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;
        s = m->user;
#else
	return 0;
#endif
    }
    usprintf(s,FromSysop,argv[2]);
    usflush(s);

    return 0;
}

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

/* write a message to all nodeshell users
 * argv[1] is the message.
 */
int
dowriteall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct mbx *m;
int i;

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


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

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

    tprintf(Nosversion,Version);
    tputs(Version2);
#ifdef UNIX
    tprintf("Tty: %d rows, %d columns\n",Numrows,Numcols);
#else
    tprintf("Tty: %d rows, %d columns\n",(int)SCREENlength,(int)SCREENwidth);
#endif

#ifdef	MSDOS
    tprintf(NosLoadInfo, _CS, _DS);
#endif
    tprintf("\nThe system time is %s", ctime(&nowtime));
	tprintf("TNOS was started on %s\n", ctime(&StartTime));
	secs = elapsedtime % 60;
	elapsedtime = elapsedtime / 60;
	mins = elapsedtime % 60;
	elapsedtime = elapsedtime / 60;
	hrs = elapsedtime % 24;
	elapsedtime = elapsedtime / 24;
	days = elapsedtime;
	tprintf("Elapsed time => %u days:%02u hours:%02u minutes:%02u seconds.\n\n",days,hrs,mins,secs);
	tprintf("Sessions = %-d, Sockets = %-d\n\n", Nsessions, Nusock);
#ifdef ALLCMD
    tprintf("The station is currently %sttended.\n", Attended ? "A" : "Una");
	tprintf("The 'Message Of The Day' is ");
	if(Motd != NULLCHAR)
		tprintf("\n%s",Motd);
	else
		tprintf("not set!\n");
#endif
#ifdef  __TURBOC__
	dofstat();		/* print status of open files */
#endif
	return 0;
}

#ifdef ALLCMD
int
domotd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc > 2) {
		tprintf("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])+5);	/* allow for the EOL char etc */
		strcpy(Motd, argv[1]);
		strcat(Motd, "\n");			/* add the EOL char */
	}
	return 0;
}
#endif /*ALLCMD*/

#ifdef nope
/* test the stktrace utility */
int dotest(int argc, char *argv[], void *p) {

    void *ptr = (void *) 0x12345678;
    free(ptr);
    return 0;
}
#endif

#ifdef	__TURBOC__
/*
 * Fstat utility code.
 * Converted to go into NOS by Kelvin Hill - G1EMM  April 9, 1990
 */

extern unsigned char _osmajor;

static char    *localcopy __ARGS((char DFAR *));
static char    *progname __ARGS((unsigned int));

int
dofstat()
{
	union REGS regs;
	struct SREGS segregs;
	char DFAR *pfiletab, DFAR * pnext, DFAR * fp;
	char DFAR *name, file[13], DFAR * plist, DFAR * entry;
	char ownername[9], ownerext[5];
	int nfiles, i, j, numhandles, entrylen;
	unsigned int access, devinfo, progpsp;
	long length, offset;
	int heading = 0;

	regs.h.ah = 0x52;	/* DOS list of lists */
	intdosx(&regs, &regs, &segregs);

	/* make a pointer to start of master list */
	plist = (char DFAR *) MK_FP(segregs.es, regs.x.bx);

	/* pointer to start of file table */
	pfiletab = (char DFAR *) MK_FP(*(int DFAR *) (plist + 6), *(int DFAR *) (plist + 4));

	switch (_osmajor) {
	case 2:
		entrylen = 40;	/* DOS 2.x */
		break;
	case 3:
		entrylen = 53;	/* DOS 3.x */
		break;
	case 4:
	case 5:			/* DOS 5.x - like dos 4.x */
	case 6:			/* DOS 6.x - like dos 4.x */
		entrylen = 59;	/* DOS 4.x - I do not know what is in the
				 * extra 6 bytes */
		break;
	default:
/*
        tprintf("Sorry, cannot handle this version of MS-DOS");
*/
        return 1;
	}

	for (;;) {
		/* pointer to next file table */
		pnext = (char DFAR *) MK_FP(*(int DFAR *) (pfiletab + 2), *(int DFAR *) (pfiletab + 0));
		nfiles = *(int DFAR *) (pfiletab + 4);
#ifdef DEBUG
		tprintf("\nFile table at %Fp entries for %d files\n", pfiletab, nfiles);
#endif
		for (i = 0; i < nfiles; i++) {

			/*
			 * cycle through all files, quit when we reach an
			 * unused entry
			 */
			entry = pfiletab + 6 + (i * entrylen);
			if (_osmajor >= 3) {
				name = entry + 32;
				strncpy(file, localcopy(name), 11);
				file[11] = '\0';
				numhandles = *(int DFAR *) (entry + 0);
				access = (int) *(char DFAR *) (entry + 2);
				length = *(long DFAR *) (entry + 17);
				offset = *(long DFAR *) (entry + 21);
				devinfo = *(int DFAR *) (entry + 5);
				progpsp = *(int DFAR *) (entry + 49);
			} else {
				name = entry + 4;
				strncpy(file, localcopy(name), 11);
				file[11] = '\0';
				numhandles = (int) *(char DFAR *) (entry + 0);
				access = (int) *(char DFAR *) (entry + 1);
				length = *(long DFAR *) (entry + 19);
				offset = *(long DFAR *) (entry + 36);
				devinfo = (int) *(char DFAR *) (entry + 27);
			}
			if ((strlen(file) > 0) && (numhandles > 0) && !(devinfo & 0x80)) {
				if(!heading) {
					tprintf("\n");
					tprintf("                 Table of Open Files.\n");
					tprintf("                 --------------------\n");
					tprintf("Name           length   offset hnd acc PSP device type/owner\n");
					tprintf("----           ------   ------ --- --- --- -----------------\n");
					heading++;		/* header now printed */
				}
				tprintf("%8.8s.%3.3s %8ld %8ld  %2d ",
				       file, &file[8], length, offset, numhandles);
				switch (access) {
				case 0:
					tprintf("r  ");
					break;
				case 1:
					tprintf("w  ");
					break;
				case 2:
					tprintf("rw ");
					break;
				default:
					tprintf("   ");
				}
				if (_osmajor >= 3)
					tprintf("%04X ", progpsp);
				else
					tprintf("---- ");
				tprintf("drive %c: ", 'A' + (devinfo & 0x1F));
				if (devinfo & 0x8000)
					tprintf("(network) ");
				if (_osmajor >= 3) {
					/*
					 * only DOS 3+ can find out
					 * the name of the program
					 */
					fnsplit(progname(progpsp), NULL, NULL, ownername, ownerext);
					tprintf("   [%s%s]\n", strlwr(ownername), strlwr(ownerext));
				} else {
					tprintf("\n");
				}
			}
			if (strlen(file) == 0)
				return 0;
		}
		pfiletab = pnext;
	}
}

/* Make a copy of a string pointed to by a far pointer */
static char *
localcopy(s)
char DFAR *s;
{
	static char localstring[256];
	char DFAR *p = s;
	char *l = localstring;
	int i = 0;

	while (*p != NULL && i++ < 255) {
		*l++ = *p++;
	}

	*l = '\0';

	return (localstring);
}

/*
 * Return a near pointer to a character string with the full path name of the
 * program whose PSP is given in the argument.  If the argument is invalid,
 * this may return gibberish but I don't know how to tell Offset 0x2C in the
 * PSP in the segment address of the environment of a program.  Beyond the
 * last environment string is a null marker, a word count (usually 1), then
 * the full pathname of the owner of the environment This only works for DOS
 * 3+
 */
static char *
progname(pid)
unsigned int pid;
{
	unsigned DFAR   *envsegptr;	/* Pointer to seg address of
					 * environment */
	char DFAR       *envptr;	/* Pointer to pid's environment	 */
	unsigned DFAR   *envsizeptr;	/* Pointer to environment size */
	unsigned        envsize;/* Size of pid's environment */
	unsigned        ppid;	/* Parent psp address */

	/* find the parent process psp at offset 0x16 of the psp */
	ppid = *(unsigned DFAR *) MK_FP(pid, 0x16);

	/* find the environment at offset 2Ch of the psp */
	envsegptr = (unsigned DFAR *) MK_FP(pid, 0x2C);
	envptr = (char DFAR *) MK_FP(*envsegptr, 0);

	/*
	 * Make a pointer that contains the size of the environment block.
	 * Must point back one paragraph (to the environments MCB plus three
	 * bytes forward (to the MCB block size field).
	 */
	envsizeptr = (unsigned DFAR *) MK_FP(*envsegptr - 1, 0x3);
	envsize = *envsizeptr * 16;	/* x 16 turns it into bytes */

	while (envsize > 0) {
		/* search for end of environment block, or NULL */
		while (--envsize && *envptr++);

		/*
		 * Now check for another NULL immediately following the first
		 * one located and a word count of 0001 following that.
		 */
		if (!*envptr && *(unsigned DFAR *) (envptr + 1) == 0x1) {
			envptr += 3;
			break;
		}
	}

	if (envsize > 0) {
		/* Owner name found - return it */
		return (localcopy(envptr));
	} else {
		if (pid == ppid) {
			/*
			 * command.com doesn't leave it's name around, but if
			 * pid = ppid then we know we have a shell
			 */
			return ("-shell-");
		} else {
			return ("unknown");
		}
	}
}

#endif /*__TURBOC__*/



/* Command history, see also pc.c - WG7J */
void logcmd(cmd)
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 ()
{
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(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    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 _FAR *_Cdecl fopen(const char _FAR *__path, const char _FAR *__mode);

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

#ifndef __TURBOC__
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
#endif /* UNIX */
#endif

#if defined(__TURBOC__) && !defined(__BORLANDC__)
/* N5KNX: TurboC 2.0 lacks _setcursortype(), which we supply here for dorepeat */
void _setcursortype(style)
int style;
{
/* From TurboC++ conio.h: */
#define _NOCURSOR      0
#define _SOLIDCURSOR   1
#define _NORMALCURSOR  2

/* Int 0x10 reg cx codes: */
#define   STD_CURSOR      0x0607
#define   BLK_CURSOR      0x0006
#define   NO_CURSOR       0x2607

union REGS regs;

 if (style == _NOCURSOR) regs.x.cx = NO_CURSOR;
 else if (style == _SOLIDCURSOR) regs.x.cx = BLK_CURSOR;
 else if (style == _NORMALCURSOR) regs.x.cx = STD_CURSOR;
 else return;
 regs.x.ax = 0x0100; /* set cursor */
 int86(0x10, &regs, &regs);
}
#endif

/* Repeat a command - taken from 930104 KA9Q NOS
   WA3DSP 1/93
*/
int
dorepeat(argc,argv,p)
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[2],REPEAT,0)) == NULLSESSION){
    tputs("Too many sessions\n");
	return 1;
    }
    _setcursortype(_NOCURSOR);
    while(sp==Current){
	/*  clrscr(); */
	/* gotoxy seems to work better - turn cursor off?? */
	gotoxy(1,1);
	ret = subcmd(Cmds,argc,argv,p);
	if(ret != 0 || mspause(interval) == -1)
	    break;
    }
    _setcursortype(_NORMALCURSOR);
    freesession(sp);
    return 0;
}


