#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <dos/dos.h>
#include <dos/var.h>
#include <clib/dos_protos.h>
/*#include <proto/dos.h>*/
#include <pragmas/dos_pragmas.h>
#include <xpr/xproto.h>
#include <xpr/xpr_lib.h>

#include "/lib/iomodes.h"
#include "/lib/misc.h"

#define memclr(a,p) memset((void*)a,0,(int)p)

/* sysghost commands */
#define COM_GET		'g'		/* get IO env/sys value */
/* we are using just this one! */

/*char portname[13]="AX-Sysghost";*/


#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2

#define MAXFILES 30	/* max number of files to send in one pass */


void __regargs __chkabort(void);
void __regargs __chkabort(void){}
void chkabort(void);
VOID chkabort(VOID) {}	/* don't allow ctrl-c/d/e/f -exit (to avoid keying errors) */


static const char vers[] = "$VER: XPR 1.22 (13.4.95)\n";

extern  struct DosLibrary *DOSBase;
struct	Library *XProtocolBase;
struct	XPR_IO io;
char	*filenames[MAXFILES];
int		numfiles=0;

char	*ProtoFile = "AXsh:etc/xprotos.tab";
char	aika[8], selection[100], username[18], name[120]="", buffer[120]="", mode[6];
long	bytes=0, size=0, cps=0, abort_transfer=0;


/* Prototypes for all routines */
void getuser(char *, int, char *, int);
void Updatelog(char *, char *);
void convert_date(char *, struct DateStamp *);
BPTR __asm __saveds c_fopen(register __a0 char *, register __a1 char *);
void __asm __saveds c_fclose(register __a0 BPTR);
long __asm __saveds c_fread(register __a0 char *, register __d0 long, register __d1 long, register __a1 BPTR);
long __asm __saveds c_fwrite(register __a0 char *, register __d0 long, register __d1 long, register __a1 BPTR);
long __asm __saveds c_finfo(register __a0 char *, register __d0 long);
long __asm __saveds c_fseek(register __a0 BPTR, register __d0 long, register __d1 long);
long __asm __saveds c_sread(register __a0 char *, register __d0 long, register __d1 long);
long __asm __saveds c_swrite(register __a0 char *, register __d0 long);
void __saveds c_sflush(void);
long __saveds c_chkabort(void);
long __asm __saveds c_ffirst(register __a0 char *, register __a1 char *);
long __asm __saveds c_fnext(register __d0 long, register __a0 char *, register __a1 char *);
long __asm __saveds c_setserial(register __d0 long);
void __saveds c_chkmisc(void);
long __saveds c_squery(void);
long __asm __saveds c_unlink(register __a0 char *);
long __asm __saveds c_options(register __d0 long, register __a0 struct xpr_option **);
long __asm __saveds c_gets(register __a0 char *, register __a1 char *);
void __asm __saveds c_update(register __a0 struct XPR_UPDATE *);
void init_xprio(int);


void getuser(char *dest, int dlen, char *home, int hlen)
{
	/* if we have <2.0 dos-library, we need sysghost */
	if(DOSBase->dl_lib.lib_Version < 36)
	{
		who(COM_GET, NULL, "HOME", home);
		who(COM_GET, NULL, "USER", dest);
	}
	else
	{
		GetVar("HOME", home, hlen, GVF_LOCAL_ONLY);
		GetVar("USER", dest, dlen, GVF_LOCAL_ONLY);
	}
}


void Updatelog(char *file, char *comment)
{
	BPTR fp;

	if(!(fp = Open(file, MODE_OLDFILE)))
	{
		if(fp = Open(file, MODE_NEWFILE))
		{
			Write(fp, "Date     Size  CPS Username   Mode Protocol   Filename\n\n", 56);
		}
		else
		{
			return;
		}
	}
	else
	{
		Seek(fp, 0, OFFSET_END);
	}
	Write(fp, comment, strlen(comment));
	Close(fp);
}

void convert_date(char *a, struct DateStamp *v)
{
	register int year=1978, day, month=1;
	const unsigned int dpm[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

	day = v->ds_Days;
	while(day >= (365+((year%4)==0 && (year%100))))
	{
		day -= 365+((year%4)==0 && (year%100));
		year++;
	}
	while(day >= (dpm[month-1]+((year%4)==0 && (year%100) && month==2)))
	{
		day -= dpm[month-1]+((year%4)==0 && (year%100) && month==2);
		month++;
	}
	day++;
	year = year%100; /* use only tens and ones */

	a[0] = day/10 + '0';
	a[1] = day%10 + '0';
	a[2] = month/10+'0';
	a[3] = month%10+'0';
	a[4] = year/10+ '0';
	a[5] = year%10+ '0';
	a[6] = '\0';
}


BPTR __saveds __asm c_fopen(register __a0 char *name, register __a1 char *mode)
{
	BPTR file;
	char dir=mode[0], mod=mode[1];

	if(dir=='a')
	{
		if(mod=='+')
			file = Open(name, MODE_READWRITE);
		else
			file = Open(name, MODE_OLDFILE);

		if(file)
			Seek(file, 0, OFFSET_END);
	}
	else if(dir=='r')
	{
		if(mod=='+')
			file = Open(name, MODE_READWRITE);
		else
			file = Open(name, MODE_OLDFILE);
	}
	else if(dir=='w')
	{
		file = Open(name, MODE_NEWFILE);
	}
	return file;
}

void __saveds __asm c_fclose(register __a0 BPTR file)
{
	Close(file);
}

long __saveds __asm c_fread(register __a0 char *buffer,
							register __d0 long size,
							register __d1 long count,
							register __a1 BPTR file)
{
	return Read(file, buffer, size*count);
}

long __saveds __asm c_fwrite(register __a0 char *buffer,
							 register __d0 long size,
							 register __d1 long count,
							 register __a1 BPTR file)
{
	return Write(file, buffer, size*count);
}

long __saveds __asm c_finfo(register __a0 char *name,
							register __d0 long info)
{	long file, len=0L;

	if(info==1)
	{
		if(file = Open(name, MODE_OLDFILE))
		{
			(void)Seek(file, 0L, OFFSET_END);
			len = Seek(file, 0L, OFFSET_CURRENT);
			Close(file);
		}			/* vain tiedoston pituuden tutkiminen */
	}
	if(info==2)
	{
		len = 1; /* binary file */
	}
	return(len);
}

long __saveds __asm c_fseek(register __a0 BPTR fileptr,
							register __d0 long position,
							register __d1 long origin)
{
	switch(origin)
	{
	case SEEK_SET:
		Seek(fileptr, position, OFFSET_BEGINNING);
		break;
	case SEEK_CUR:
		Seek(fileptr, position, OFFSET_CURRENT);
		break;
	case SEEK_END:
		Seek(fileptr, position, OFFSET_END);
		break;
	}
	return 0L;
}


/* Get char from stdio  */
/* timeout is in micros */

/* AINA PALAUTETAAN saatujen merkkien määrä tai -1==FATAL ERROR */
/* myös jos timeout tulee ennen kuin max on luettu! */
/* JOS timeout==0: lue heti max size merkkiä, kuitenkin puskuri tyhjäksi */
/* palauta saatujen merkkien määrä */

long __saveds __asm c_sread(register __a0 char *buffer,
							register __d0 long size,
							register __d1 long timeout)
{	int chars=0, c;

	while(chars<size)
	{
		if(WaitForChar(Input(), timeout))
		{
			c = Read(Input(), buffer+chars, size-chars);
			if(c <= 0)
			{
				abort_transfer = -1;
				return -1;	/* carrier lost? EOF? breaked?*/
			}
			chars += c;
			if(!timeout)
				return chars;
		}
		else
		{
			return chars;
		}
	}
	return chars;
}

/* Put string to stdout */

long __saveds __asm c_swrite(register __a0 char *buffer,
							 register __d0 long size)
{
	return Write(Output(), buffer, size);
}

/* Flush stdin buffer*/

void __saveds c_sflush(void)
{
	char flushbuffer[128], count=0;

	while(WaitForChar(Input(), 0) &&
		  ++count<20 &&
		  Read(Input(), flushbuffer, 128)>0);
}

long __saveds c_chkabort(void)
{
#if 0
	long state=iostatus();

	if(state==-1)	return 0;		/* ACTION_QUERY not implemented */
	if(state & (1<<5))	return -1;	/* carrier lost */
	if(state & (1<<10))	return -1;	/* break received */
#endif
	if(abort_transfer)
		return -1;
	return 0;
}

long __saveds __asm c_ffirst(register __a0 char *buf,
							 register __a1 char *pattern)
{
	if(!numfiles)
		return 0;

	strcpy(buf, filenames[0]);
	return 1;
}

long __saveds __asm c_fnext(register __d0 long fstate,
							register __a0 char *buf,
							register __a1 char *template_arg)
{
	if(fstate >= numfiles)
		return 0;
	strcpy(buf, filenames[fstate]);
	return fstate+1;
}

/* query/set serial params */
long __saveds __asm c_setserial(register __d0 long newstatus)
{
	if(newstatus==-1L)
	{
		return 0x00040094;
		/* 4800bps, parity off, 7WIRE, RAD_BOOGIE, XON/XOFF disabled, 8bits */
	}
	else
	{
		return -1L;
	}
}

/* check miscellaneous stuff */
void __saveds c_chkmisc(void)
{;
}

/* query serial input buffer */
long __saveds c_squery(void)
{
#if 1
	/* A patch to make it work with the antique axaux-handler
	   and AmiTCP's TCP-handler. */
	long chars = WaitForChar(Input(), 0);

	if((chars==-1))
	{
		chars = IoErr();
	}
	return chars;
#else
	if(WaitForChar(Input(), 0))
	{
		return IoErr();
	}
	return 0;
#endif
}

/* delete a file */

long __saveds __asm c_unlink(register __a0 char *name)
{
	return DeleteFile(name);
}

long __saveds __asm c_options(register __d0 long n,
							  register __a0 struct xpr_option *opt[])
{
	return 0;
}


long __saveds __asm c_gets(register __a0 char *prompt,
						   register __a1 char *buffer)
{
	setmode(MODE_CONSOLE);
	Write(Output(), prompt, strlen(prompt));
	Read(Input(), buffer, 20);
	setmode(MODE_RAW | MODE_NOCRLF);
	return 0;
}

void __saveds __asm c_update(register __a0 struct XPR_UPDATE *ud)
{
	long a = ud->xpru_updatemask;

	if((a & XPRU_FILENAME))
		strcpy(name, ud->xpru_filename);
	if((a & XPRU_FILESIZE))
		size = ud->xpru_filesize;
	if((a & XPRU_BYTES))
	{
		bytes = ud->xpru_bytes;
		if(bytes==size && bytes)
		{
			struct DateStamp v;
			int j;

			DateStamp(&v);
			convert_date(aika, &v);

#if 0 /* Do not shorten the filename paths */
			if((j=strlen(name))>32)
			{
				j-=32;
				while(name[j]!='/' && name[j]!=':' && name[j])
					++j;
			}
			else
#endif
				j=0;

			sprintf(buffer, "%s %6ld %4ld %-10s %s %-10s %s\n",
					aika,
					bytes,
					cps,
					username,
					mode,
					selection,
					name+j);
			Updatelog("AXsh:etc/adm/Xpr.log", buffer);
			size = -1;	/* already updated */
		}
	}
	if((a & XPRU_DATARATE))
		cps = ud->xpru_datarate;
}


void init_xprio(int batch)
{
	memclr((void*)&io, sizeof(io));

#pragma msg 147 ignore push
	io.xpr_filename= NULL;
	io.xpr_fopen =	(void *) c_fopen;
	io.xpr_fclose=	(void *) c_fclose;
	io.xpr_fread =	(void *) c_fread;
	io.xpr_fwrite=	(void *) c_fwrite;
	io.xpr_sread =	(void *) c_sread;
	io.xpr_swrite=	(void *) c_swrite;
	io.xpr_sflush=	(void *) c_sflush;
	io.xpr_update=	(void *) c_update;
	io.xpr_chkabort=(void *) c_chkabort;
/*	io.xpr_chkmisc=	(void *) c_chkmisc;*/
	io.xpr_gets =	(void *) c_gets;
	io.xpr_setserial=(void*) c_setserial;

	if(batch)
	{
		io.xpr_ffirst=	(void *) c_ffirst;
		io.xpr_fnext=	(void *) c_fnext;
	}
	io.xpr_finfo=	(void *) c_finfo;
	io.xpr_fseek=	(void *) c_fseek;

	io.xpr_extension=	3L;

/*	io.xpr_options=	(void *) c_options;*/
	io.xpr_unlink=	(void *) c_unlink;
	io.xpr_squery=	(void *) c_squery;
#pragma msg 147 pop
}

#define LINELEN	100

void main(int argc, char *argv[])
{
	char libname[LINELEN]="",
		options[LINELEN]="",
		desc[LINELEN]="",
		homedir[LINELEN]="AXsh:tmp/",
		line[LINELEN],
		buffer[LINELEN+2];
	int i, pos, found;
	long lock;
	BPTR fp;

	if(argc==2 && (argv[1][0]=='l' || argv[1][0]=='L'))
	{
		printf("%s\nAvailable protocols:\n", vers+6);
		if(fp = Open(ProtoFile, MODE_OLDFILE))
		{
			pos = 0;
			while(pos += Read(fp, buffer+pos, LINELEN-pos))
			{
				i = 0;
				while(i<pos && buffer[i]!='\n')
					++i;
				buffer[i] = '\0';

				strcpy(line, buffer);
				movmem(buffer+i+1, buffer,pos-i-1);
				pos -= i+1;

				if(line[0]=='\"')
					printf("%s\n", line);
				if(!strncmp(line, "Descrip: ", 9))
					printf("    %s\n", line+9);
			}
			Close(fp);
		}
		exit(4);
	}

	if(argc<3 || 
		(argv[1][0]!='s' && argv[1][0]!='S' && argv[1][0]!='r' && argv[1][0]!='R'))
	{
		printf("Usage:  XPR send protocol filename [file2 ..]\n        XPR receive protocol [filename]\n        XPR list\n");
		exit(5);
	}

	if(fp = Open(ProtoFile, MODE_OLDFILE))
	{
		found = pos = 0;
		while(pos += Read(fp, buffer+pos, LINELEN-pos))
		{
			i = 0;
			while(i<pos && buffer[i]!='\n')
				++i;
			buffer[i] = '\0';

			strcpy(line, buffer);
			memcpy(buffer, buffer+i+1, LINELEN-i-1);
			pos -= i+1;

			if(!found)
			{
				if(line[0]=='\"' && !strncmp(line+1, argv[2], strlen(argv[2])))
					found = -1;
			}
			else
			{
				if(!strncmp(line, "Library: ", 9))
					strcpy(libname, line+9);
				if(!strncmp(line, "Descrip: ", 9))
					strcpy(desc, line+9);
				if(!strncmp(line, "Options: ", 9))
					strcpy(options, line+9);
			}
			if(found && libname[0] && options[0])
				break;
		}
		Close(fp);
	}

	if(!libname[0])
	{
		printf("No transfer type \"%s\"\n", argv[2]);
		exit(20);
	}
	if(!options[0])
	{
		printf("No options for \"%s\"\n", argv[2]);
		exit(20);
	}

	getuser(username, LINELEN, homedir, LINELEN);
	strncpy(selection, argv[2], 9);
	selection[10] = '\0';

	if(desc[0])
		printf("\"%s\" : \"%s\"\nStart transfer.\n", argv[2], desc);

	if(argv[1][0]=='r' || argv[1][0]=='R')
	{
		strcpy(mode, "Rcvd");
		if(argc>4)
			printf("Only one file or batch transfer!\n");

		if(options[strlen(options)-1]=='P')
		{
			strcat(options, homedir);
		}

		if((XProtocolBase = (struct Library *)OpenLibrary(libname, 0)))
		{
			if(argc>3)
			{
				init_xprio(FALSE);
			}
			else
			{
				init_xprio(TRUE);
			}

			io.xpr_filename = options;

			if(XProtocolSetup(&io))
			{
				setmode(MODE_RAW | MODE_NOCRLF);
				io.xpr_filename = "";

				if(argc>3)
					io.xpr_filename = argv[3];
				name[0] = '\0';

				XProtocolReceive(&io);
				XProtocolCleanup(&io);
				setmode(MODE_CONSOLE);
			}
			else
			{
				printf("Setup error\n");
			}
			CloseLibrary(XProtocolBase);
		}
		else
		{
			printf("\aCannot open %s\n", libname);
			exit(20);
		}
	}
	else
	{
		strcpy(mode, "Send");
		for(i=3;i<argc && numfiles<MAXFILES-1;i++)
		{
			if(lock = Lock(argv[i], ACCESS_READ))
			{
				UnLock(lock);
				filenames[numfiles++] = argv[i];
			}
			else
			{
				printf("Cannot open %s, not sending\n", argv[i]);
			}
		}

		if(numfiles)
		{
			if((XProtocolBase = (struct Library *)OpenLibrary(libname, 0)))
			{
				if(numfiles>1)
				{
					init_xprio(TRUE);
				}
				else
				{
					init_xprio(FALSE);
				}

				io.xpr_filename = options;

				if(XProtocolSetup(&io))
				{
					setmode(MODE_RAW | MODE_NOCRLF);
					io.xpr_filename = "";
					name[0] = '\0';

					if(numfiles==1)
						io.xpr_filename = filenames[0];

					XProtocolSend(&io);
					XProtocolCleanup(&io);
					setmode(MODE_CONSOLE);
				}
				else
				{
					printf("Setup error\n");
				}
				CloseLibrary(XProtocolBase);
			}
			else
			{
				printf("\aCannot open %s\n", libname);
				exit(20);
			}
		}
		else
		{
			printf("No files to send!?\n");
		}
	}
	exit(0);
}

