/* Main-level NOS program:
 *  initialization
 *  keyboard processing
 *  generic user commands
 *
 * Copyright 1991 Phil Karn, KA9Q
 */

/****************************************************************************
*	$Id: main.c 1.5 94/01/04 14:09:37 ROOT_DOS Exp $
*	14 Jul 93	1.4		GT	Banner changes.									*
*	07 Nov 93	1.5		GT	Prompt global.									*
*
*	ATARI Version by David Nash - dnash@chaos.demon.co.uk
*
*  __stdargs dosourceb, domore
*  dosource, dosurceb call do_source to avoid reg/std call sequecne problems
*
****************************************************************************/

#include <stdio.h>
#include <time.h>
#if	defined(__TURBOC__) && defined(MSDOS)
#include <io.h>
#include <conio.h>
#endif
#include "global.h"
#ifdef	ANSIPROTO
#include <stdarg.h>
#endif

#ifdef   ATARI
#include <unistd.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 far Cmds[], Startcmds[],Stopcmds[],Attab[];

#ifdef MSDOS
int Escape = 324;	/* MSDOS escape character is F10 */
#else
#ifdef ATARI
int Escape = 0x44;	/* default excape character is F10 */
#else
char Escape = 0x1d;	/* default escape character is ^] */
#endif
#endif

char Badhost[] = "Unknown host %s\n";
char *Hostname;
char Nospace[] = "No space!!\n";	/* Generic malloc fail message */
struct proc *Cmdpp;
struct proc *Display;
int main_exit = FALSE;			/* from main program (flag) */

#ifndef ATARI
char Prompt[] = "net> ";
#else
char Prompt[] = "\x08\x08NET > ";
#endif
static FILE *Logfp;
static time_t StartTime;		/* time that NOS was started */
static int Verbose;

static char banner1[] = "Based on KA9Q NOS by Phil Karn - %s\n";
static char banner2[] = "Copyright 1991 Phil Karn.  ";
static char banner3[] = "Parts copyright 1992, 1993 Demon Internet & Members\n\n";
static char banner4[] = "Support: mail internet@demon.net or phone 081 343 3881\n";

int main(int argc, char *argv[])
{
	char *inbuf,*intmp;
	FILE *fp;
	struct daemon *tp;
	struct mbuf *bp;
	int c;
	int rootset = 0;

	StartTime = time(&StartTime);

	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 */
			rootset = 1;
			initroot(optarg);
			break;
		case 'b':	/* Use BIOS for screen output */
#ifdef	__TURBOC__
			directvideo = 0;
#endif
			break;
		case 'v':
			Verbose = 1;
			break;
		case '?':										/* illegal option found			*/
			printf("usage: %s [-s num] [-d dir] [-v] [startup] \n", argv[0]);
			printf("where num = number of sockets, default=%d \n", Nusock);
			printf("      dir = root directory for nos, default=\\ \n");
			printf("      -v = verbose flag \n");
			printf("      startup = startup file, default=%s\n", Startup);

			return -1;                       	/* give up and exit			 	*/ 			
		}
	}

	if(!rootset) {										/* set route from env - DFN 	*/
		char *root;

		if ((root = getenv("NOS_ROOT")))
			initroot(root);
		else
			initroot("\\");
	}

	kinit();
	ipinit();
	ioinit();
	sockinit();
	Cmdpp = mainproc("cmdintrp");

	Sessions = (struct session *)callocw(Nsessions, sizeof(struct session));
	Command  = Lastcurr = newsession("command interpreter", COMMAND);
	Display  = newproc("display", 250, display, 0, NULLCHAR, NULL, 0);

	tprintf("\033E");								/* clear the screen               */
	tprintf("%s %s %s\n\n", atari_ver1, atari_ver2, atari_ver3);
	tprintf(banner1, Version);
	tprintf(banner2);
	tprintf(banner3);
/* 
	tprintf(banner4);
*/
   log(-1,"Load address %X \n", main);	
	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],strerror(errno));
	} else {
		fp = fopen(Startup,READ_TEXT);
	}
	if(fp != NULLFILE){
		inbuf = mallocw(BUFSIZ);
		intmp = mallocw(BUFSIZ);
		while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){
			strcpy(intmp,inbuf);
			if(Verbose)
				tprintf("%s",intmp);
			if(cmdparse(Cmds,inbuf,NULL) != 0){
				tprintf("input line: %s",intmp);
			}
		}
		fclose(fp);
		free(inbuf);
		free(intmp);
	}
	/* 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);
		}
	}
	return 0;
}


/*
	keyboard - Keyboard input process
*/

void keyboard(int i, void *v1, void *v2)
{
	int c;
	struct mbuf *bp;

	Current->cur_pos = 0;

	/* Keyboard process loop */

	for( ; ; ) {
		c = kbread();
		if (c == -2 && Current != Command) {

			/* Save current tty mode and set cooked */
#ifndef ATARI			
			swapscreen(Current,Command);
#else
			alert(Display, 1);
#endif			
			Lastcurr = Current;
			Current = Command;
		}
		
		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,c)) != NULLBUF) {
				send_mbuf(Current->input, bp, 0, NULLCHAR, 0);
				Current->cur_pos = 0;
			}
		}
	}
}


/*
	dodelete - Delete a file
*/

int dodelete(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],strerror(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",strerror(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;

				/* remove trailing dot */
				if ( Hostname[strlen(Hostname)] == '.' ) {
					Hostname[strlen(Hostname)] = '\0';
				}
				tprintf("Hostname set to %s\n", name );
			}
		} else {
			if(Hostname != NULLCHAR)
				free(Hostname);
			Hostname = strdup(argv[1]);
		}
	}
	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 was started at %s", ctime(&StartTime));
#ifdef ATARI
		log(-1, "NOS Load address: %x ", main);
#endif		
#ifdef MSDOS
		log(-1,"NOS load information: CS=0x%04x DS=0x%04x", _CS, _DS);
#endif
	}
	return 0;
}

/*
	dosource - stub function for do_source with __regargs linkage
*/

int dosource(int argc, char *argv[], void *p)
{
	return do_source(argc, argv, p);
}

/*
	dosourceb - stub function for do_source with _-stdargs linkage
*/

int __stdargs dosourceb(int argc, char *argv[], void *p)
{
	return do_source(argc, argv, p);
}

/*
	do_source - replaces dosource in pc ka9q, called via stub functions to
	            fixup the linkage.
*/
	            
static int do_source(int argc, char *argv[], void *p)
{
	int linenum = 0;
	char *inbuf,*intmp, *p1, *p2;
	FILE *fp;

	/* Read command source file */
	if((fp = fopen(argv[1],READ_TEXT)) == NULLFILE){
        tprintf("Can't read %s: %s\n",argv[1],strerror(errno));
		return 1;
	}

	p1 = inbuf = malloc(BUFSIZ);
	p2 = 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(p1);
	free(p2);
	return 0;
}

int
dohelp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct cmds *cmdp;
	int i;
	char buf[66];

	tprintf(banner1, Version);
	tprintf(banner2);
	tprintf(banner3);
	tprintf(banner4);
	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;
{
	register struct iface *ifp;
	int param,set;
	int32 val;

	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;
}

int
doescape(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2)
		tprintf("%d\n",Escape);
	else if (strlen(argv[1]) == 1)
		Escape = *argv[1];
	else Escape = atoi(argv[1]);
	return 0;
}

/* 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':
			addr = resolve(optarg);
			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)
		len += sizeof(int32);

	if(key != NULLCHAR)
		len += klen;

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

	fsock.sin_family = AF_INET;
	fsock.sin_addr.s_addr = resolve(host);
	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",strerror(errno));
		goto cleanup;
	}
cleanup:
	if(data != &x)
		free(data);
	close_s(s);
	return 0;
}
int __stdargs 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)) == 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;
}
/* 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;
#if defined(MSDOS) || defined(ATARI)
	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);
#if defined(MSDOS) || defined(ATARI)
	/* 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

