/* Main-level network program:
 * initialization
 * keyboard processing
 * generic user commands
 *
 * Copyright 1991 Phil Karn, KA9Q
 */
#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
#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"
#include "enet.h"
#include "netrom.h"
#include "ftpcli.h"
#include "telnet.h"
#include "tty.h"
#include "session.h"
#include "hardware.h"
#include "usock.h"
#include "socket.h"
#include "cmdparse.h"
#include "commands.h"
#include "daemon.h"
#include "devparam.h"
#include "domain.h"
#include "files.h"
#include "main.h"
#include "remote.h"
#include "trace.h"

extern struct cmds Cmds[],Startcmds[],Stopcmds[],Attab[];

#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 Badhost[] = "Unknown host %s\n";
char *Hostname = NULLCHAR;
char Nospace[] = "No space!!\n";	/* Generic malloc fail message */
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;

static char *DumpAddr = NULL;		/* Memory dump pointer */
static char Prompt[] = "net> ";
static FILE *Logfp;
static time_t StartTime;		/* Time that NOS was started */
static int Verbose;

static void ctohex __ARGS((char *buf,int16 c));
static void fmtline __ARGS((int16 addr,char *buf,int16 len));

int
main(argc,argv)
int argc;
char *argv[];
{
	char *inbuf,*intmp;
	FILE *fp;
	struct daemon *tp;
	struct mbuf *bp;
	int c;
	int i;

	StartTime = time(&StartTime);			/* NOS Start_Up time */

	while((c = getopt(argc,argv,"s:d:bv")) != EOF){
		switch(c){
		case 's':	/* Number of sockets */
			Nusock = atoi(optarg);
			break;
		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;
		}
	}
	kinit();
	ipinit();
	ioinit();
	sockinit();
	Cmdpp = mainproc("cmdintrp");

	Sessions = (struct session *)callocw(Nsessions,sizeof(struct session));
	Command = Lastcurr = newsession("command interpreter",COMMAND,0);
	Command->flowmode = 1;		/* set 'more' paging on command screen */
	Display = newproc("display",250,display,0,NULLCHAR,NULL,0);
	tprintf("KA9Q NOS version %s\n%s\n",Version, Version2);
	tprintf("Copyright 1990 by Phil Karn (KA9Q) and others.\n");
	rflush();

	/* Start background Daemons */
	for(tp=Daemons;;tp++){
		if(tp->name == NULLCHAR)
			break;
		newproc(tp->name,tp->stksize,tp->fp,0,NULLCHAR,NULL,0);
	}

	if(optind < argc){
		/* Read startup file named on command line */
		if((fp = fopen(argv[optind],READ_TEXT)) == NULLFILE)
			tprintf("Can't read config file %s: %s\n",
			 argv[optind],sys_errlist[errno]);
	} else {
		/* Read default startup file named in files.c (autoexec.nos) */
		if((fp = fopen(Startup,READ_TEXT)) == NULLFILE)
			tprintf("Can't read default config file %s: %s\n",
			 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);
			}
		}
		fclose(fp);
		free(inbuf);
		free(intmp);
	}

	Mprunning = 1;	/* we are on speed now */

	/* Now loop forever, processing commands */
	for(;;){
		tprintf(Prompt);
		usflush(Command->output);
		if(recv_mbuf(Command->input,&bp,0,NULLCHAR,0) != -1){
			(void)cmdparse(Cmds,bp->data,Lastcurr);
			free_p(bp);
		}
	}
}
/* Keyboard input process */
void
keyboard(i,v1,v2)
int i;
void *v1;
void *v2;
{
	int c;
	struct mbuf *bp;

	/* 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;
			/* set 'more' paging on command screen */
			Command->flowmode = 1;
		}

		Current->row = MOREROWS;
		psignal(&Current->row,1);
		if(c >= 0){
			/* 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,(char)c)) != NULLBUF){
				send_mbuf(Current->input,bp,0,NULLCHAR,0);
			}
		}
	}
}
/* Standard commands called from main */
int
dodelete(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int i;

	for(i=1;i < argc; i++){
		if(unlink(argv[i]) == -1){
			tprintf("Can't delete %s: %s\n",
			 argv[i],sys_errlist[errno]);
		}
	}
	return 0;
}
int
dorename(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(rename(argv[1],argv[2]) == -1)
		tprintf("Can't rename: %s\n",sys_errlist[errno]);
	return 0;
}
int
doexit(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int i;
	time_t StopTime;

	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 */
        shuttrace();
	log(-1,"NOS was stopped at %s", ctime(&StopTime));
	if(Logfp){
		fclose(Logfp);
		Logfp = NULLFILE;
	}
	iostop();
	exit(0);
	return 0;	/* To satisfy lint */
}

int
dohostname(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	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)] == '.')
				Hostname[strlen(Hostname)] = '.';
		}
	}
	return 0;
}

int
dolog(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	static char *logname;

	if(argc < 2){
		if(Logfp)
			tprintf("Logging to %s\n",logname);
		else
			tprintf("Logging off\n");
		return 0;
	}
	if(Logfp){
		log(-1,"NOS log closed");
		fclose(Logfp);
		Logfp = NULLFILE;
		free(logname);
		logname = NULLCHAR;
	}
	if(strcmp(argv[1],"stop") != 0){
		logname = strdup(argv[1]);
		Logfp = fopen(logname,APPEND_TEXT);
		log(-1,"NOS v%s was started at %s", Version, ctime(&StartTime));
#ifdef	MSDOS
		log(-1,"NOS load information: CS=0x%04x DS=0x%04x", _CS, _DS);
#endif
	}
	return 0;
}
int
dohelp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct cmds *cmdp;
	int i;
	char buf[66];

	tprintf("Main commands:\n");
	memset(buf,' ',sizeof(buf));
	buf[64] = '\n';
	buf[65] = '\0';
	for(i=0,cmdp = Cmds;cmdp->name != NULL;cmdp++,i = (i+1)%4){
		strncpy(&buf[i*16],cmdp->name,strlen(cmdp->name));
		if(i == 3){
			tprintf(buf);
			memset(buf,' ',sizeof(buf));
			buf[64] = '\n';
			buf[65] = '\0';
		}
	}
	if(i != 0)
		tprintf(buf);
	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("Interface \"%s\" unknown\n",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("Interface \"%s\" unknown\n",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 = DATAGRAM_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() */
	while((c = getopt(argc,argv,"a:p:k:s:")) != EOF){
		switch(c){
		case 'a':
			if((addr = resolve(optarg)) == 0){
				tprintf("Host %s unknown\n",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];
	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(len);

	fsock.sin_family = AF_INET;
	if((fsock.sin_addr.s_addr = resolve(host)) == 0){
		tprintf("Host %s unknown\n",host);
		goto cleanup;
	}
	fsock.sin_port = port;

	switch(cmd[0]){
	case 'r':
		data[0] = SYS_RESET;
		if(key != NULLCHAR)
			strncpy(&data[1],key,klen);
		break;
	case 'e':
		data[0] = SYS_EXIT;
		if(key != NULLCHAR)
			strncpy(&data[1],key,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;
}
int
domore(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct session *sp;
	FILE *fp;
	char buf[81];
	int row;

	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;
	if((fp = fopen(argv[1],READ_TEXT)) == NULLFILE){
		tprintf("Can't read %s\n",argv[1]);
		keywait(NULLCHAR,1);
		freesession(sp);
		return 1;
	}
	row = MOREROWS;
	while(fgets(buf,sizeof(buf),fp),!feof(fp)){
		tprintf("%s",buf);
		if(--row == 0){
			row = keywait("--More--",0);
			switch(row){
			case -1:
			case 'q':
			case 'Q':
				goto done;
			case '\n':
			case '\r':
				row = 1;
				break;
			case ' ':
			default:
				row = MOREROWS;
			}
		}
	}
done:	fclose(fp);
	keywait(NULLCHAR,1);
	freesession(sp);
	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;

	buffer = callocw(2000, sizeof (char));

	if ((handle = open (argv[1], O_BINARY | O_RDONLY)) == -1) {
		tprintf("Can't open input file %s: %s\n",
		 argv[1],sys_errlist[errno]);
		free(buffer);
		return -1;
	}
	length = filelength(handle);

	if (length > 2000) {
		length -= 2000;
	} else {
		rdsize = (int) length;
		length = 0;
	}

	lseek (handle, length, SEEK_SET);
	if (read (handle, buffer, rdsize) == -1) {
		tprintf("Can't read input file %s: %s\n",
		 argv[1],sys_errlist[errno]);
		close(handle);
		free(buffer);
		return -1;
	}

	for (i = rdsize - 1; i > 0; i--) {
		if (buffer[i] == '\n')
			line++;
		if (line == 18)
			break;
	}
	for (; i < rdsize; i++)
		tputc(buffer[i]);

	tprintf("\n");
	close(handle);
	free(buffer);
	return 0;
}

/* No-op command */
int
donothing(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return 0;
}

/* 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

	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," - ");
	va_start(ap,fmt);
	vfprintf(Logfp,fmt,ap);
	va_end(ap);
	fprintf(Logfp,"\n");
	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;

	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," - ");
	fprintf(Logfp,fmt,arg1,arg2,arg3,arg4,arg5);
	fprintf(Logfp,"\n");
	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;

	/* Read command source file */
	if((fp = fopen(argv[1],READ_TEXT)) == NULLFILE){
		tprintf("Can't read source file %s: %s\n",
		 argv[1],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 */
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 */
dothirdparty(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setbool(&ThirdParty,"Third-Party mail flag",argc,argv);
}

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;
	}
	tprintf("            Main Memory Dump Of Location %Fp\n", addr);
	tprintf("Addr (offset)           Hexadecimal                         Ascii\n");
	tprintf("----                    -----------                         -----\n\n");

	for(i = 0; i < len; i += 16)
		fmtline(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)];
}

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("KA9Q Internet Protocol Package, v%s\n%s\n\n",Version, Version2);

#ifdef	MSDOS
	tprintf("NOS load information:   Code Segment = %04x - Data Segment = %04x\n\n", _CS, _DS);
#endif
	tprintf("The system time is %s", ctime(&nowtime));
	tprintf("NOS 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("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");
#ifdef	__TURBOC__
	dofstat();		/* print status of open files */
#endif
	return 0;
}

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;
}

#ifdef	__TURBOC__
/*
 * Fstat utility code.
 * Converted to go into NOS by Kelvin Hill - G1EMM  April 9, 1990
 */

extern unsigned char _osmajor;

static char    *localcopy(char far *);
static char    *progname(unsigned int);

int
dofstat()
{
	union REGS regs;
	struct SREGS segregs;
	char far *pfiletab, far * pnext, far * fp;
	char far *name, file[13], far * plist, far * 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 far *) MK_FP(segregs.es, regs.x.bx);

	/* pointer to start of file table */
	pfiletab = (char far *) MK_FP(*(int far *) (plist + 6), *(int far *) (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 */
		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 far *) MK_FP(*(int far *) (pfiletab + 2), *(int far *) (pfiletab + 0));
		nfiles = *(int far *) (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 far *) (entry + 0);
				access = (int) *(char far *) (entry + 2);
				length = *(long far *) (entry + 17);
				offset = *(long far *) (entry + 21);
				devinfo = *(int far *) (entry + 5);
				progpsp = *(int far *) (entry + 49);
			} else {
				name = entry + 4;
				strncpy(file, localcopy(name), 11);
				file[11] = '\0';
				numhandles = (int) *(char far *) (entry + 0);
				access = (int) *(char far *) (entry + 1);
				length = *(long far *) (entry + 19);
				offset = *(long far *) (entry + 36);
				devinfo = (int) *(char far *) (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 far *s;
{
	static char localstring[256];
	char far *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 far   *envsegptr;	/* Pointer to seg address of
					 * environment */
	char far       *envptr;	/* Pointer to pid's environment	 */
	unsigned far   *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 far *) MK_FP(pid, 0x16);

	/* find the environment at offset 2Ch of the psp */
	envsegptr = (unsigned far *) MK_FP(pid, 0x2C);
	envptr = (char far *) 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 far *) 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 far *) (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
