/*file: bpl.c
 *	(c) 1989, Phinloda and SHU (FMR version)
 *
 *	PC98, 286, J3100, FMR version
 *		Borland C++ v2.0 (Borland JAPAN)
 *	or, J3100 version
 *		Tucbo-C++ v1.0 (Borland)
 *	SunOS v4.0.3
 *		gcc v1.37.1 (FSF)
 *
 *	890911	v1
 *	890912	v2		option v, t, h
 *	890913	v3		bug fix
 *	890915	v4		send ACK for all packet / reenter filename
 *	890915	v5		re-enter when downloading
 *	890916	v6		DEBUG mode modification
 *	890916	v7		support 300, 1200 bps
 *	890918	v8		exit when 1st B session end
 *	890918	v9		restore messages to Japanese
 *	890918	v10		rename b.c from bp.c
 *	890930	v11		remove bplus.c
 *	891001	v12		fix v11 bugs
 *	891005	v13		fix v12 bugs, rename to bpl.exe
 *	891007	v14		time estimate
 *	891009	v15		estimate
 *	891014	v16		TI packet
 *	891014	v17		my_bplus_entry changed
 *	900225	v18		-a, d, e options
 *	900303	v19		-a debug, adjust file name if it is invalid
 *	900303	v20		change -h message
 *	900303	v21		BPL= environment bug, -d bug
 *	900305	v22		-d bug
 *	900306	v23		-t bug, change bytes, time message
 *	900430	v24		-a, upload
 *	900512	v25
 *	900516	v26		-o Flying-B+ :-)
 *	900520	v27		full rewriting
 *	900525	v28		+ packet bug, some messages
 *	900526	v29		time estimation bug
 *	900606	v30		-p option (FMR)
 *	900607	v31		-p option (J3100)
 *	900608	v32		-1, -3 bug
 *	900609	v33		FMR rsraw setup
 *	900610	v34		-i option
 *	900623	v35		FMR debug
 *	900628	v36		Failue packet bug
 *	900817	v36a	機種依存部分を分離 (M.Aza)
 *	900907	v37		test version (UNIX only)
 *	901105	v38		copy_body() bug fix
 *	901127	v39		check over-run
 *	901229	v40		dubug!, change the method of packet allocation
 *	901230	v41		numeric optoin, change _stklen
 *	910103	v42		file size message
 *	910105	v43		English version
 *	910115	v44		initiator mode, resume upload
 *	910122	v45		time stump, stopchar
 *	910127	v46		pc98, RTS flow control
 *	910129	v47		debug, 
 *	910215	v48		upload retry bug
 *	911209	v49		resume download bug
 *  920714	v50		resume downlodo bug
 *	920823	v51		compiled with ..
 *	921212	v52		-j option
 */


#include <stdio.h>
#include <stdlib.h> /* malloc */
#include <string.h> /* strlen */

#include "bpl.h"
#include "machine.h"

#define	MYBUFSIZE	4096
#define	MYDIRSIZE	256

#define bps300  1
#define bps1200 4
#define bps2400 8

#ifdef __TURBOC__
extern unsigned _stklen = 10000; /* default is 4096 */
static char borlandc_copyright_notice[] =
	"Compiled with Borland C++\n"
	"(Borland C++  Version 2.0 Copyright (c) 1991 Borland International)\n";
#endif

extern int com_send(unsigned char ch);
extern int com_setup(char *s, unsigned int size);
extern int execute_bpl_session(APPL_PARAM *);
extern void com_close(void);

extern char rsraw_id_string[]; /* defined in rsraw.c */
extern char rsraw_writers[]; /* defined in rsraw.c */

static FNAME_LIST *last = NULL;
static char	mybuf[MYBUFSIZE] = {
	0, /* port number */
	1, /* flow-control */
	0, /* hard flow-control */
	0, /* default = 11 (2400 bps) */
	0  /* default = 8 (packet size = 1024 bytes) */
};

static APPL_PARAM my_param =
	{1, 0, bps2400, 0xff, 4,
	FLAGS_SKIPENQ|FLAGS_ONLY_ONCE|FLAGS_SETFTIME, NULL, NULL};

static char mydir[MYDIRSIZE];
static char	version[] = "BPL.EXE v52, by Phinloda\n";
static int send_ctrlq = 0;
static int silent = 0;

#ifdef	DEBUG
FILE *debug_fp = NULL;
#endif

#define	STS_IS_NORMAL 0
#define	NEXT_ARG_IS_DIR 1

#ifdef JAPANESE

/* set_options() */
#define str_unknown_opt1 "オプション "
#define str_unknown_opt2 " は解釈できません\n"
#define str_usage "\n使い方: bpl [-オプション] [ファイル名]\n"

/* add_filename_list() */
#define str_out_of_memory "メモリが足りません\n"

/* main() */
#define str_bpl_start "−BPL 処理開始−\n"
#define str_bpl_end "−BPL 処理終了−\n"

#else

#define str_unknown_opt1 "Unknown option (-"
#define str_unknown_opt2 ")\n"
#define str_usage "\nUsage: bpl [-OPTIONS] [filename]\n"
#define str_out_of_memory "Out of memory\n"
#define str_bpl_start "- BPL start -\n"
#define str_bpl_end "- BPL end -\n"

#endif

#ifndef USE_ORIGNAL_CONSOLE_FUNC

/*--------------------------------------------------------------------*/
void charwarn(int c)
{
	putc(c, stderr);
}

/*--------------------------------------------------------------------*/
void strwarn(char *s)
{
	fputs(s, stderr);
}

/*--------------------------------------------------------------------*/
void charout(int c)
{
	putc(c, stderr);
}

/*--------------------------------------------------------------------*/
void strout(char *s)
{
	fputs(s, stderr);
}

/*--------------------------------------------------------------------*/
/*	printout decimal integer
 *	not thinking about when i == -32768
 */
void decout(int i)
{
	char buf[6];
	int j;

	if (i < 0) {
		putchar('-');
		i = -i;
	}
	else if (i == 0) {
		putchar('0');
		return;
	}

	buf[5] = '\0';
	for (j = 4; ; j--) {
		buf[j] = ((i % 10) + '0');
		i /= 10;
		if (i == 0 || j == 0)
			break;
	}
	strout(buf + j);
}

/*--------------------------------------------------------------------*/
/*	printout decimal long
 */
void ldecout(unsigned long l)
{
	char buf[11];
	int i;

	if (l == 0) {
		putchar('0');
		return;
	}

	buf[10] = '\0';
	for (i = 9; ; i--) {
		buf[i] = ((l % 10) + '0');
		l /= 10;
		if (l == 0 || i == 0)
			break;
	}
	strout(buf + i);
}

#endif

/*--------------------------------------------------------------------*/
static int compare_string(char *s1, char *s2)
{
	char c1;
	char c2;

	do {
		c1 = *s1++;
		if (c1 == ' ' || c1 == '\t')
			c1 = (char) 0;
		c2 = *s2++;
		if (c2 == ' ' || c2 == '\t')
			c2 = (char) 0;
	} while ((c1 == c2) && (c1 != (char) 0));

	return (int) c1 - c2;
}

/*--------------------------------------------------------------------*/
static char *speed_table[] = {
	"1S50",
	"1S75",
	"1S110",
	"1S134",
	"1S150",
	"1S200",
	"1S300",
	"2S600",
	"4S1200",
	"6S1800",
	"8S2400",
	"8S4800",
	"8S9600",
	"8S19200",
	"8S38400",

	"1P128",
	"2P256",
	"4P512",
	"8P1024",
	NULL
};

static int numeric_option(char *s)
{
	char **table;
	int i;

	i = 0;
	for (table = speed_table; *table != NULL; table++) {
		if (compare_string(s, *table + 2) == 0) {
			if (*(*table + 1) == 'S') {
				mybuf[3] = i + 1; /* speed */
				if (mybuf[4])
					goto skip;
			}
			my_param.my_bps = mybuf[4] = **table - '0';
skip:
			return 1;
		}
		i++;
	}
	return 0;
}

/*--------------------------------------------------------------------*/
/*	return value
 *		0	nothing special
 *		1	next argument is directory name
 */
static int set_options(char *s)
{
	char c;
	int i;
	int sts = STS_IS_NORMAL;

	while ((c = *s) != 0) {
		if (c >= '0' && c <= '9') {
			i = numeric_option(s);
			if (i) {
				while (*s >= '0' && *s <= '9')
					s++;
				continue;
			}
		}

		s++;

		if (c >= 'A' && c <= 'Z')
			c += 'a' - 'A';
		switch (c) {
#ifdef	DEBUG
		case 'z':
			my_param.verbose = 3;
			break;
#endif

		case 'a': /* append mode */
			my_param.append_mode = 1;
			break;

		case 'b': /* binary */
			my_param.flags |= FLAGS_BINARY;
			break;

		case 'c':
			my_param.flags &= ~FLAGS_ONLY_ONCE;
			break;

		case 'd': /* set directory */
		case 'u': /* currently dummy .. */
			if (*s == ' ')
				s++; /* ? */

			if (*s) {
				for (i = 0; i < MYDIRSIZE - 1; i++) {
					if (*s == '\0')
						break;
					if (*s == ' ') {
						s++;
						break;
					}
					mydir[i] = *s++;
				}
				mydir[i] = '\0';
				my_param.dirname = (UCHAR *) mydir;
			}
			else {
				sts = NEXT_ARG_IS_DIR;
			}
			break;

		case 'e':
			my_param.flags &= ~FLAGS_SKIPENQ;
			break;

		case 'f': /* force */
			my_param.flags |= FLAGS_FORCE;
			break;

		case 'i': /* interval, or initiator */
			if (*s >= '0' && *s <= '9') {
				my_param.est_interval = *s - '0';
				s++;
			}
#ifdef WITH_INITIATOR
			else {
				my_param.flags |= FLAGS_INITIATOR;
				if (*s == 'u') {
					my_param.flags |= FLAGS_UPLOAD;
					s++;
				}
			}
#endif
			break;

		case 'j': /* look */
			my_param.flags &= ~FLAGS_SETFTIME;
			break;

		case 'l': /* look */
			my_param.verbose |= 4;
			break;

		case 'n': /* No flow control in com_send */
			mybuf[1] = 0;
			break;

		case 'o': /* flying CRC */
			my_param.flags |= FLAGS_FLY;
			break;

		case 'p': /* change communication Port */
			if (*s >= '0' && *s <= '9') { /* 920808 ヨナ */
				mybuf[0] = (char) (*s - '0');
				s++;
			} else {
				mybuf[0] = (char) 1;
			}
			break;

		case 'q': /* ^Q */
			send_ctrlq = 1;
			break;

#ifdef WITH_RESUME
		case 'r': /* Resume */
			my_param.flags |= FLAGS_RESUME;
			break;
#endif

		case 't': /* terse */
			my_param.verbose = 0;
			/* fall into next case */

		case 's': /* silent */
			silent = 1;
			break;

		case 'v': /* verbose */
			my_param.verbose |= 2;
			break;

		case '5': /* check CS */
			mybuf[2] = 1;
			break;

		case '7': /* mask parity */
			my_param.maskchar = 0x7f;
			break;

		case '-': /* ignore '-' */
		case ' ': /* ignore ' ' */
			break;

		default:
			strwarn(str_unknown_opt1);
			charwarn(c);
			strwarn(str_unknown_opt2);
			/* fall into next case */

#ifdef __TURBOC__
		case 'x':
			strwarn(borlandc_copyright_notice);
#endif
		case 'h': /* help */
			strwarn(version);
			strwarn("for CompuServe B Plus Protocol (SM)\n");
			strwarn(rsraw_id_string);
			strwarn(", rsraw.c by ");
			strwarn(rsraw_writers);
			strwarn(str_usage);
			exit(1);
		}
	}

	return sts;
}

/*--------------------------------------------------------------------*/
static void add_filename_list(char *name)
{
	FNAME_LIST *tmp;

	tmp = (FNAME_LIST *) malloc(strlen(name) + sizeof(FNAME_LIST));
	if (tmp == NULL) {
		strwarn(str_out_of_memory);
		exit(2);
	}

	tmp->next = NULL;
	strcpy(tmp->name, name);

	if (my_param.fnlist == NULL) {
		my_param.fnlist = tmp;
	}
	else {
		last->next = tmp;
	}

	last = tmp;
}

/*--------------------------------------------------------------------*/
static void options(int argc, char *argv[])
{
	char *s;
	int sts = 0;

	while (--argc > 0) {
		s = *++argv;
		if (sts == NEXT_ARG_IS_DIR) {
			sts = 0;
			my_param.dirname = (UCHAR *) s;
		}
		else {
			if (*s == '-') {
				sts = set_options(s);
			}
			else {
				add_filename_list(s);
			}
		}
	}

	if (mybuf[3] == 0)
		mybuf[3] = 11; /* default = 2400bps */
	if (mybuf[4] == 0)
		my_param.my_bps = mybuf[4] = 8; /* default = 1024 bytes */
}

/*--------------------------------------------------------------------*/
static void checkenv(char *env[])
{
	char *s;

	while ((s = *env++) != 0) {
		if (s[0] == 'B' && s[1] == 'P' && s[2] == 'L' && s[3] == '=') {
			set_options(s + 4);
		}
	}
}

/*--------------------------------------------------------------------*/
int main(int argc, char *argv[], char *env[])
{
	int sts;

	checkenv(env);
	options(argc, argv);

	/* com_setup エラー時には中断する */
	if (com_setup(mybuf, MYBUFSIZE)) { /* setup interrupt vector */
		exit(1);
	}

	if (my_param.verbose) {
		strout(str_bpl_start);
	}

#ifdef	DEBUG
	debug_fp = NULL;
	if (my_param.verbose > 2) {
		debug_fp = fopen("temp.dat", "wb");
	}
#endif

	sts = execute_bpl_session(&my_param);

#ifdef	DEBUG
	if (my_param.verbose > 2) {
		fclose(debug_fp);
	}
#endif

	if (send_ctrlq) {
		com_send((char) ('Q'-'@')); /* oops .. */
	}
	com_close(); /* restore interrupt vector */
	if (my_param.verbose > 0) {
		if (!silent)
			charout('G' - '@');
		charout('\n');
		strout(str_bpl_end);
	}
	exit(sts);
}
