/*  Program in Small-C to convert a regular text file to Wordstar
 *   'document' format with soft spaces and carriage returns.
 *
 *  Version 1.0 by Gordon Brandly, R.R.2, Fort Sask., AB, CANADA  T8L 2N8
 *
 * 02/19/84 -  Ver. 2.0 Rewritten for C/80 Ver. 3.0
 *
 * Added command mode and enhanced interactive mode.
 *
 * In interactive mode, the program checks all characters as they're input 
 * to see if they're permitted in a filename.  If not, error messages are
 * displayed and the user may try again.  The program also checks for the
 * existence of the input file and lets the user try again if the file's
 * not found.  Finally, the program checks for the ability to create the
 * output file.  If there's no disk or directory space available for
 * output, user may change disks and continue.
 *
 * NOTE:  Ver. 2.0 is completely rewritten from Ver. 1.0.  The only similar-
 * ities are the name and function of the program.  Otherwise, the two
 * programs bear no resemblance.  
 *
 * NOTE:  Avoiding printf saves about 2k in the executable file.
 *
 * Compile with the -c switch:   C -c1680 d:ENSOFT
 *
 *	Harvey G. Lord, Box 183, Storrs, CT  06268  Tel. 203-429-8044
 */

#define EOF	-1
#define NULL	'\0'
#define FALSE	NULL
#define TRUE	1
#define LF	0x0A
#define RESET	0x0D		 /* CP/M's BDOS disk reset function	*/
#define CPM_EOF	0x1A
#define DEL	0x7F

main(argc,argv)
int argc;
char *argv[];
{
	extern char Cmode;		/* Single byte I/O char in C/80	*/
	char infile[15], outfile[15];
	int fdes_in, fdes_out;		/* File descriptors		*/
	static char *signon[] =
	{
	"\nENSOFT Ver. 2.0 by Harvey G. Lord 02/19/1984\n",
	"Converts \'hard formatted\' text files to WordStar document files.\n",
	NULL
	};
	static char *instructions[] =
	{
	"\nCommand mode usage:\n\n",
	"ENSOFT d:input.fil d:output.fil\n\n",
	"Interactive mode usage:\n\n",
	"ENSOFT\n\n",
	"Interactive mode prompts for inputs and checks for permissible file\
names.\n\n",
	"You are in interactive mode now.\n\n",
	"CTRL-B (^B) aborts anywhere in the program.\n\n",
	"NOTE:  Handing ENSOFT non-ASCII files produces nonsense.\n\n",
	"Press any key to continue.\n\n",
	NULL
	};
	Cmode = 0;			/* Single byte I/O		*/

	display(signon);

	if(argc == 3)			/* Arguments on command line	*/
	{
		*infile = NULL;		/* Remove garbage from filenames */
		*outfile = NULL;
		strcat(infile,argv[1]);	/* Make argv[1] & infile same	*/
		strcat(outfile,argv[2]); /* Make argv[2] & outfile same	*/
	}
	else
	{
		display(instructions);
		getchar();		/* Wait for keypress		*/
		get_file(infile,TRUE);	/* Get infile name		*/
		get_file(outfile,FALSE); /* Get outfile name		*/
	}

	compare(infile,outfile);	/* Are infile & outfile the same?
					If so, compare issues error message
					and starts over.		*/
	while((fdes_in = file(infile,"r",TRUE)) == NULL) /* Open input	*/
	{
		get_file(infile,TRUE);	/* If can't open, try again.	*/
		bdos(RESET,"");		/* Reset disk drives		*/
	}

/* NOTE:  The output file must be opened in "wb" or "write binary" mode be-
cause the "translated" or ASCII mode prevents writing a bare linefeed into
the file.  Writing a bare LF must be done after every soft carriage return. */

	if(!(fdes_out = file(outfile,"wb",FALSE))) /* Open output file	*/
	{				/* If can't open ...		*/
		fclose(fdes_in);	/* Close input file		*/
		bdos(RESET,"");		/* Reset disk drives		*/
		main(NULL,NULL);	/* Rerun			*/
	}
	puts("\nPlease wait.  I\'m converting ");
	puts(infile);
	puts(" to WordStar\'s \'document\' format.\n\n");
	ensoft(fdes_in,fdes_out);	/* Process and write file	*/
	puts(outfile);
	puts(", the converted file, is written.  Job done.\n");
	exit();
}					/* End of main			*/

get_file(array,i)			/* Gets filename in array	*/
char *array;
int i;					/* i is either TRUE or FALSE	*/
{                                       /* TRUE = input; FALSE = output */
	char drv[3], fname[10], ftype[4];
	*array = NULL;			/* Remove garbage from array	*/

	getdrv(drv,i);			/* Get in/output file's drive	*/
	getfname(fname,i);		/* Get in/output file's name	*/
	getftype(ftype,i);		/* Get in/output file's type	*/
	strcat(array,drv);		/* Put 3 arrays together to ... */
	strcat(array,fname);		/* make in/outfile's whole name */
	strcat(array,ftype);
}					/* End of get_file		*/

getdrv(array,j)				/* Get drive designator	in array */
char *array;
int j;					/* j is either TRUE or FALSE	*/
{					/* TRUE = input; FALSE = output */


	static char *errmes[] =
	{
	" is not a valid drive designator.  Only A and B are permitted.\n",
	"Please try again.\n",
	NULL
	};
	int c, i;			/* i is array subscript		*/
	i = 0;				/* Initialize subscript		*/

	if(j)
		puts("\nDisk drive where input file resides (no colon):  ");
	else
		puts("\nDisk drive to put output file (no colon):        ");
	c = getchar();
	if(c == 'A' || c == 'a' || c == 'B' || c == 'b')/* Legal drive? */
	{
		if(islower(c))		/* If char is lower case	*/
		{
			c = toupper(c); /* Convert to upper case	*/
			putchar('\b');
			putchar(c);	/* Put upper case on screen	*/
		}
		array[i++] = c;		/* Drive letter	...		*/
		array[i++] = ':';	/*  followed by colon		*/
		array[i] = NULL;	/* Close array			*/
	}
	else				/* Illegal designator		*/
	{
		puts("\n\nI\'m sorry, ");
		putchar(c);
		display(errmes);
		getdrv(array,j);
	}
}					/* End of getdrv		*/

getfname(array,j)			/* Get filename in array	*/
char *array;
int j;					/* j is either TRUE or FALSE	*/
{                                       /* TRUE = input; FALSE = output */
	static char *errmes[] =
	{
	" in a file\'s name.\n",
	"Please re-enter.\n",
	NULL
	};
	int c, i;			/* i is array subscript		*/
	i = 0;				/* Initialize subscript		*/

	if(j)
		puts("\nInput file\'s name + <cr> (8 characters max.):    ");
	else
		puts("\nOutput file\'s name + <cr> (8 characters max.):   ");
	while ((c = getchar()) != '\n') /* Get fname & put it in array	*/
	{
		if(c == '\b' || c == DEL) /* Backspace or ASCII 7Fh	*/
		{
			if(i == 0)	/* Beginning of name		*/
				; 	/* Don't move			*/
			else		/* Not beginning of name	*/
			{
				if(c == DEL) /* ASCII 7Fh		*/
					putchar('\b'); /* Backspace	*/
				putchar(' '); /* Write a space		*/
				putchar('\b'); /* Backspace		*/
				--i;	/* Decrement subscript		*/
			}
		}
		else if(i == 8)		/* Max allowed in filename	*/
		{
			puts("  <== 8 chars max!\n");
			break;
		}
		else if(check(c))	/* Is char permitted in fname?	*/
		{
			if(islower(c))	/* If c is lower case		*/
			{
				c = toupper(c); /* Convert to upper case */
				putchar('\b'); /* Backspace		*/
				putchar(c); /* Put upper case char on screen */
			}
			array[i++] = c;	/* Put it in array		*/
		}
		else			/* If not, display errmes	*/
		{
		puts("\n\nI\'m sorry, CP/M does not permit ");
		putchar(c);
		display(errmes);
		getfname(array,j);	/* And give another chance	*/
		}
	}				/* End of while			*/
	array[i++] = '.';		/* Put period into array	*/
	array[i] = NULL;		/* Close array			*/
}					/* End of getfname		*/

getftype(array,j)			/* Get file type in array	*/
char *array;				
int j;                                  /* j is either TRUE or FALSE	*/
{                                       /* TRUE = input; FALSE = output */
	static char *errmes[] =
	{
	" in a file\'s type.\n",
	"Please re-enter.\n",
	NULL
	};
	int c, i;			/* i is array subscript		*/
	i = 0;				/* Initialize array		*/

	if(j)
		puts("Input file\'s type + <cr> (3 characters max.):    ");
	else
		puts("Output file\'s type + <cr> (3 characters max.):   ");
	while ((c = getchar()) != '\n') /* Get fname & put it in array	*/
	{
		if(c == '\b' || c == DEL) /* Backspace or ASCII 7Fh	*/
		{
			if(i == 0)	/* Beginning of name		*/
				; 	/* Don't move			*/
			else		/* Not beginning of name	*/
			{
				if(c == DEL) /* ASCII 7Fh		*/
					putchar('\b'); /* Backspace	*/
				putchar(' '); /* Write a space		*/
				putchar('\b'); /* Backspace		*/
				--i;	/* Decrement subscript		*/
			}
		}
		else if(i == 3)		/* Max allowed in filetype	*/
		{
			puts("       <== 3 chars max!\n");
			break;
		}


		else if(check(c))	/* Is char permitted in ftype?	*/
		{
			if(islower(c))	/* If c is lower case		*/
			{
				c = toupper(c); /* convert to upper case */
				putchar('\b'); /* Backspace		*/
				putchar(c); /* Put upper case char on screen */
			}
			array[i++] = c;	/* Put it in array		*/
		}
		else			/* If not, display errmes	*/
		{
		puts("\n\nI\'m sorry, CP/M does not permit ");
		putchar(c);
		display(errmes);
		getftype(array,j);	/* And give another chance	*/
		}
	}				/* End of while			*/
	array[i] = NULL;		/* Close array			*/
}					/* End of getftype		*/

check(c)				/* Is c permitted in filename?	*/
int c;					/* Returns TRUE or FALSE	*/
{
	if(isalpha(c))			/* Permitted in CP/M fname	*/
		return TRUE;
	if(isdigit(c))			/* Permitted in CP/M fname	*/
		return TRUE;
	switch(c)
	{
		case '!':		/* Permitted in CP/M fname	*/
		case '`':		/* Permitted in CP/M fname	*/
		case '#':		/* Permitted in CP/M fname	*/
		case '$':		/* Permitted in CP/M fname	*/
		case '%':		/* Permitted in CP/M fname	*/
		case '&':		/* Permitted in CP/M fname	*/
		case '(':		/* Permitted in CP/M fname	*/
		case ')':		/* Permitted in CP/M fname	*/
		case '\'':		/* Permitted in CP/M fname	*/
		case '-':		/* Permitted in CP/M fname	*/
		case '@':		/* Permitted in CP/M fname	*/
		case '^':		/* Permitted in CP/M fname	*/
		case '{':		/* Permitted in CP/M fname	*/
		case '}':		/* Permitted in CP/M fname	*/
		case '~':		/* Permitted in CP/M fname	*/
			return TRUE;
			break;
		default:		/* Illegal character found	*/
			return FALSE;
	}
}					/* End of check			*/

compare(infile,outfile)			/* Traps same in & out file error */
char *infile, *outfile;
{
	static char *errmes[] =
	{
	"\n\nSorry, your input file\'s name and your output file'\s name \
are identical.\n",
	"CP/M doesn\'t permit two files with the same name on the same \
disk.\n\n",
	"We\'ll have to go back to the beginning and start over.\n\n",
	"Please press any key to continue.  CTRL-B (^B) aborts.\n\n\n\n",
	NULL
	};

	if(strcmp(infile,outfile) == NULL) /* If in & outfile are same	*/
	{
		display(errmes);	/* Error!			*/
		getchar();		/* Wait for keypress		*/
		main(NULL,NULL);	/* Try again			*/
	}
}					/* End of compare		*/

file(fname,mode,j)			/* Open file in designated mode	*/
char *fname, *mode;
int j;					/* j is either TRUE or FALSE	*/
{                                       /* TRUE = input; FALSE = output */
	int fdes;
	static char *errmesr[] =
	{
	"Please check your spelling and be sure the file you\n",
	"want is on the designated disk.\n",
	NULL
	};
	static char *errmesw[] =
	{
	"The disk directory is probably full.  Unfortunately, this error\n",
	"requires starting over.  You may insert a new output disk now,\n",
	"if necessary.\n\n",
	NULL
	};

	if((fdes = fopen(fname,mode)) > NULL)	/* Open file		*/
		return fdes;		/* Successful open		*/
	puts("\nSorry, I can\'t open "); /* Error in open		*/
	puts(fname);
	puts(".\n");
	if(j)				/* Input file to be opened	*/
		display(errmesr);
	else				/* Output file to be opened	*/
		display(errmesw);
	puts("Please press any key to try again.  CTRL-B (^B) aborts.\n");
	getchar();
	return NULL;			/* Tell caller of error		*/
}					/* End of file			*/

ensoft(fdes_in,fdes_out)		/* Process file			*/
int fdes_in, fdes_out;
{
	int c;
	int cr_flag, space_flag, start_line;
	cr_flag = space_flag = start_line = NULL;

	while((c = getc(fdes_in)) != EOF) /* Get chars from input	*/
	{
		if(c == '\n')
		{
			start_line = NULL; /* Zero start_line flag	*/
			if(!cr_flag)	/* 1st CR/LF			*/
			{
				++cr_flag;
				if(space_flag)	/* Space before CR/LF?	*/
					space_flag = NULL; /* Zero flag */
			}
			else if(cr_flag == 1)	/* 2nd CR/LF in a row	*/
			{

				putc('\r',fdes_out); /* Put in 1st one	*/
				putc(LF,fdes_out);
				putc('\r',fdes_out); /* Save this, too	*/
				putc(LF,fdes_out);
				++cr_flag;
			}
			else			/* 3rd or > CR/LF in row */
			{
				putc('\r',fdes_out); /* Save it		*/
				putc(LF,fdes_out);
			}
		}
		else if(c == ' ')
		{
			if(!start_line)	      /* Space at start of line */
			{
				if(cr_flag == 1) /* CR/LF before it	*/
				{    /* Probably beginning of paragraph */
					putc('\r',fdes_out);
					putc(LF,fdes_out);
				}
				cr_flag = NULL;	/* Zero the flag	*/
				putc(c,fdes_out); /* Save it		*/
			}
			else if(!space_flag)	/* 1st space NOT at start
							of line		*/
			{
				putc(c,fdes_out); /* Save it		*/
				++space_flag;
			}
			else			/* 2 or > spaces in row */
				putc((' ' | 128),fdes_out); /* Soften it */
		}
		else				/* Any other character	*/
		{
			++start_line;
			if(cr_flag == 1)	/* Last char a single CR/LF */
			{
				cr_flag = NULL; /* Zero flag		*/
				putc(('\r' | 128),fdes_out); /* Soften CR */
				putc(LF,fdes_out); /* Hard LF		*/
				putc(c,fdes_out);  /* Save character	*/
			}
			else if(cr_flag > 1)	/* Last 2 or > chars CR/LF */
			{
				cr_flag = NULL; /* Zero flag		*/
				putc(c,fdes_out); /* Save character	*/
			}
			else if(space_flag > NULL) /* Last char a space */
			{
				space_flag = NULL;  /* Zero flag	*/
				putc(c,fdes_out);   /* Save character	*/
			}
			else
				putc(c,fdes_out);   /* Save character	*/
		}
	}					/* End of while		*/

	if(cr_flag == 1)			/* CR/LF at EOF		*/
	{
		putc('\r',fdes_out);
		putc(LF,fdes_out);
	}

/* NOTE:  Since the output file was opened in binary, it must be ended with
an explicit ^Z, CPM_EOF.  For binary files, fclose does not append the ^Z. */

	putc(CPM_EOF,fdes_out);			/* End output file	*/

	fclose(fdes_in);			/* Close input file	*/
	fclose(fdes_out);			/* Close output file	*/
}						/* End of ensoft	*/

display(array_name)			/* Displays large char arrays	*/
char *array_name[];
{
	int i;

	for(i = 0;array_name[i] != NULL;i++)
		puts(array_name[i]);
}					/* End of display		*/

puts(string)				/* Display string		*/
char *string;
{
        while (*string)
		putchar(*string++); 
}					/* End of puts 			*/

#include "stdlib.c"
