/****************************************************************************
*$RevHead_Start                                                             *
*                                                                           *
* Project Details                                                           *
* ---------------                                                           *
* Project Name:    Combine                                                  *
* Project Version: 1.5                                                      *
* Copyright:       © David M. Balean 1994                                   *
* Date:            Saturday 5th March 1994                                  *
* Start DateStamp: 28-Feb-1994 22.35.37.28                                  *
* Project Manager: DMB                                                      *
*                                                                           *
*****************************************************************************
*                                                                           *
* This File:       combine.c                                                *
*$Author:          David M. Balean                                          *
*$File Version:    1.05                                                     *
*$DateStamp:       05-Mar-1994 13.52.32.42                                  *
*                                                                           *
*****************************************************************************
* File Summary:                                                             *
* This version includes a primitive input routine for use if started        *
* from Workbench.                                                           *
* Can't yet be started from a "Project" icon.                               *
* If there is a filename clash with the destination, that file is saved     *
* to a temporary file and not deleted.                                      *
*****************************************************************************
*                                                                           *
* Date        Time         Version Author                                   *
*                                                                           *
* 28-Feb-1994 22.31.31.04  1.00    David M. Balean                          *
*                                                                           *
* 01-Mar-1994 13.51.19.05  1.01    David M. Balean                          *
* Added startup from project icon.                                          *
*                                                                           *
* 02-Mar-1994 14.02.07.10  1.02    David M. Balean                          *
* Corrected a small bug in that the destination file was not always saved   *
* to a temporary file if it was going to be overwritten.                    *
* Added a "DELETE" option so that any such temporary can be automatically   *
* removed unless there was an error.                                        *
* If the file is retained the returncode is "RETURN_WARN" or "RETURN_ERROR" *
*                                                                           *
* 02-Mar-1994 19.07.01.39  1.03    David M. Balean                          *
* Added "DELETE" and "QUIET" options to Workbench start.  User still gets   *
* message to press WINDOWCLOSE or a key to quit.                            *
* Cleaned up the input routine.  There was a bug in the BACKSPACE delete.   *
*                                                                           *
* 02-Mar-1994 19.55.35.09  1.04    David M. Balean                          *
* Added output redirection from Workbench startup.  The output file is      *
* created if not already present otherwise the output is appended           *
* to the existing file as per ">>outfile" from the CLI.  This is done so    *
* that if the file already exists it's current contents are not lost!       *
* If Workbench options or redirection aren't required, leave them empty.    *
*                                                                           *
* 05-Mar-1994 13.52.32.42  1.05    David M. Balean                          *
* Corrected a very small bug in the scanning of the list file.  If there    *
* was an error in some situations it wasn't reported and the rest of the    *
* list was skipped.  Now the whole process is abandonned and the user       *
* is informed of the problem.                                               *
*                                                                           *
*$RevHead_End                                                               *
****************************************************************************/

/************************************************************************
 * COMBINE								*
 * 									*
 * Usage:								*
 *   COMBINE [FROM] {filename} [AS/TO] {filename} [Q/QUIET] [D/DELETE]	*
 * where:								*
 *	combine		is cli type command				*
 *	from		optional					*
 *	listfilename	full pathname of a list of files		*
 *	as (or to)	optional					*
 *	filename	full pathname of the file to which the		*
 *			files in the list are to be appended.		*
 *			If the file doesn't exist it is created.	*
 *			If there was an original file it is save as	*
 *			a tempoary file.  Also if one of the source	*
 *			files in the list is the same as the "as" file	*
 *			it is save as the tempoary file.		*
 *	quiet (or q)	optional, no info to user			*
 *	delete (or d)	optional, delete temporary file if no error.	*
 *									*
 *	?		display usage (if QUIET not specified)		*
 *			and abort.					*
 *									*
 * The "AS" filename should be different from any filename in		*
 * the list of filenames but if there is a clash that file is		*
 * copied to a temporary file.  If that happens the user is		*
 * informed if the quiet option isn't used.  The return code		*
 * is "WARN" (i.e. 5) if this happens or RETURN_ERROR if there		*
 * was an error.  If the "DELETE" option is used and the file		*
 * is actually deleted the the return code is RETURN_OK.		*
 *									*
 * The program changes directory to the directory of 			*
 * the listfile so paths are relative to the listfile.			*
 *									*
 * NOTE:  Input/Output re-direction is handled by AmigaDOS.		*
 * If started from the CLI (SHELL), >filename causes all output		*
 * to go to the file "filename" which is opened as a new file.		*
 * If >>filename is used, "filename" has the output appended		*
 * to it.  This program doesn't need user input from the CLI		*
 * but <filename is used for that purpose.  The >filename etc.		*			*
 * are stripped from the argument list before reaching			*
 * the program so cannot be checked by it.				*
 *									*
 ************************************************************************/

#include <exec/types.h>
#include <exec/execbase.h>
#include <workbench/startup.h>

#include <stdio.h>		/* printf etc.		*/
#include <stdlib.h>		/* EXIT_FAILURE etc.	*/
#include <string.h>		/* stricmp() etc.	*/
#include <ctype.h>		/* isalnum() etc.	*/
#include <dos.h>		/* getcd()		*/

#include <proto/dos.h>		/* Read */
#include <dos/dos.h>		/* BPTR, RETURN_OK .WARN.ERROR.FAIL */

#include "Version.h"




/* These are useful for 4 colour windows */

#define NORMAL_style_colour()	(void)(printf("\x9b" "0;31;40m"),fflush(stdout))

#define NORMAL_style()		(void)(printf("\x9b" "0m"),fflush(stdout))
#define BOLD_style()		(void)(printf("\x9b" "1m"),fflush(stdout))
#define FAINT_style()		(void)(printf("\x9b" "2m"),fflush(stdout))
#define ITALIC_style()		(void)(printf("\x9b" "3m"),fflush(stdout))
#define UNDERLINE_style()	(void)(printf("\x9b" "4m"),fflush(stdout))
#define INVERSE_style()		(void)(printf("\x9b" "7m"),fflush(stdout))


#define	HLBg_colour()		(void)(printf("\x9b" ";;42m"),fflush(stdout))
#define SHBg_colour()		(void)(printf("\x9b" ";;43m"),fflush(stdout))
#define SHFg_colour()		(void)(printf("\x9b" ";43m"),fflush(stdout))

/* NOTE: FAINT_style is HLFg_colour() */

#define	NORMAL_colour()		(void)(printf("\x9b" ";31;40m"),fflush(stdout))


/* Cursor and window editting control */
#define Insert_char()		(void)(printf("\x9b" "@"),fflush(stdout))
#define Forward_cursor()	(void)(printf("\x9b" "C"),fflush(stdout))
#define Back_cursor()		(void)(printf("\x9b" "D"),fflush(stdout))
#define scr_Beep()		(void)(printf("\x7"),fflush(stdout))





#define DEL	127		/* DELETE key */

#define	CSI	0x9B		/* [Alt][Esc]			*/

#define	CUP	'A'		/* [ CSI ][ character ]		*/
#define	CDOWN	'B'
#define	CRIGHT	'C'
#define	CLEFT	'D'
#define	CSUP	'T'
#define	CSDOWN	'S'
#define STAB	'Z'

#define	CSRIGHT	'@'		/* [ CSI ][ " " ][ character ]	*/
#define	CSLEFT	'A'

#define HELP	'?'		/* [ CSI ][ character ][ ~ ]	*/


#define	F1	'0'
#define	F2	'1'
#define	F3	'2'
#define	F4	'3'
#define	F5	'4'
#define	F6	'5'
#define	F7	'6'
#define	F8	'7'
#define	F9	'8'
#define	F10	'9'





#define UNLESS(x)	if(!(x))

void __regargs _CXBRK(void); /* Redirect Ctrl C and Ctrl D to our own cleanup */

void Usage(char *fn, int n);
void cleanexit(int rc);
int fappend(FILE *to, FILE *from);
char *getfname(FILE *fp);
struct FileNode * addflist(char *name);
int fncopy(char *to, char *from);
VOID getinput(UBYTE *buffer, int max);
VOID SaveAS(VOID);


/*****
 NOTE:	The AmigaDOS Manual 3rd Edition states on page 392:-
	CLOSE   Put a close gadget on the window.  If the user closes the
		window, a read from CON: will return -1L: a read from RAW:
		(or a CON: in raw mode) will return the Raw Event escape
		string for a Close gadget.

	Using "getch" obtains the RAW input event string.  "stdout" MUST
 	be kept flushed or the code fails to be received properly.
******/



char Version[] =
	{
	'$','V','E','R',':',' ',
	PROJECT_NAME,
	' ','v',
	PROJECT_VERSION,
	' ',
	PROJECT_COPYRIGHT,
	'\0'
	};

/* make a decent sized window if from WB */
char __stdiowin[] = 
	{
	'C','O','N',':',
	'1','0','/','1','0','/','6','0','0','/','1','0','0','/',
	PROJECT_NAME,
	' ','v',
	PROJECT_VERSION,
	};

char __stdiov37[] = "/CLOSE/AUTO";


static const char *AppErrors[] =
	{
	"",
	"Incorrect number of arguments",
	"Too many list filenames",
	"Too many destination filenames",
	"Illegal option",
	"Can't open file",
	"Copying/Appending file",
	"Reading from list",
	"List and destination files the same",
	"Not enough memory",
	"Can't close file",
	"Can't find this file",
	"Can't get this path",
	"Can't create temporary file name",
	};

#define NO_ERR		0L
#define ARG_ERR		1L
#define LST_ERR		2L
#define DST_ERR		3L
#define OPT_ERR		4L
#define OPEN_ERR	5L
#define CPYIO_ERR	6L
#define READ_ERR	7L
#define SAME_ERR	8L
#define MEM_ERR		9L
#define CLOSE_ERR	10L
#define	FIND_ERR	11L
#define GETPATH_ERR	12L
#define TEMP_ERR	13L

static struct FileNode
	{
	struct FileNode	*next;
	char		fname[0];
	};


static struct FileNode *FList;

static BOOL vflag = FALSE;	/* version of OS. TRUE for >=2		*/
static BOOL wbflag = FALSE;	/* TRUE if from Workbench.		*/
static BOOL quietflag = FALSE;	/* TRUE if no output wanted.		*/
static BOOL deleteflag = FALSE;	/* TRUE if temporary file to be deleted.*/

static BOOL fromflag = FALSE;	/* TRUE if 1st file may not be FROM	*/

static UBYTE from[256];
static UBYTE as[256];
static UBYTE redir[256];

static UBYTE opts[20];		/* Options from WB */

static char tname[L_tmpnam];	/* temporary filename */
static char thisname[256];	/* for filenames in the list */

static FILE *fromfp = NULL;
static FILE *asfp = NULL;
static FILE *out = stdout;	/* Default output. WB start can change it */

static BPTR	oldlock;		/* lock at startup	*/ 
static BPTR	workinglock = -1L;	/* -1, an invalid lock	*/



/************************** MAIN ********************************/

int main(int argc,

	union	{
		char			**args;
		struct WBStartup	*msg;
		} argv )


{
int		n;
struct FileNode	*fl;
BPTR		lock;


from[0] = as[0] = tname[0] = '\0';

/* SysBase = *(struct ExecBase **)4L; */
if ( (*(struct ExecBase **)4L)->LibNode.lib_Version >= 36)
	vflag = TRUE;

/* We have to have an existing lock! */
oldlock =
    ((struct Process *)((*(struct ExecBase **)4L)->ThisTask))->pr_CurrentDir;

/* if (argc == 0) we started from WB so gets args from user */
/* THIS COULD BE BETTER! */

if (argc == 0)
	{
	wbflag = TRUE;

/* Make sure that the drawer containing the calling icon becomes current */
/* If called from CLI, the directory is that cli's current directory	**/
/* THIS SOMETIMES FREEZES WORKBENCH but not every time...		**/
/* I can't work it out; it should be OK.				**/
/* The problem arises when the C function chdir() is used.		**/
/* Stick to AmigaDOS functions!						**/

	CurrentDir(
		(argv.msg->sm_ArgList[argv.msg->sm_NumArgs - 1].wa_Lock));

	printf("\n FROM List Filename:      ");
	getinput(from,256);

	printf(" AS Destination Filename: ");
	getinput(as,256);

	printf(" OPTIONS (D and/or Q):    ");
	getinput(opts,20);

	n = 0;
	while (opts[n])
		{
		switch (opts[n])
			{
			case 'D':
			case 'd':

			if ( (stricmp(&opts[n], "DELETE") == 0) ||
					(opts[n+1] == ' ') || !(opts[n+1]) )
				{
				deleteflag = TRUE;
				while (opts[n] && (opts[n] != ' '))
				n++;
				}

			else Usage(NULL,OPT_ERR);


			break;


			case 'Q':
			case 'q':

			if ( (stricmp(&opts[n], "QUIET") == 0) ||
					(opts[n+1] == ' ') || !(opts[n+1]) )
				{
				quietflag = TRUE;
				while (opts[n] && (opts[n] != ' '))
				n++;
				}

			else Usage(NULL,OPT_ERR);
			break;

			default:
			Usage(NULL,OPT_ERR);
			break;
			
			}

/* Remove spaces */
		while (opts[n] == ' ') n++;
		}

	printf(" Output redirection file: ");
	getinput(redir,256);

	if (redir[0])
		UNLESS (out = fopen(redir,"a"))
			Usage(redir, OPEN_ERR);
	}
else
	{

/******************* FROM CLI
	NOTE:
	The CLI itself fixes up input and output re-direction.
	>Filename is stripped from the arguments and "Filename"
	is used as stdout.
*****************************/	


	if (argc < 2)			/* minimum of 3 args */
		Usage(NULL,ARG_ERR);


/* Parse the args */
/* NOTE: "Combine FROM FROM AS AS" is legal */
/* You can't use "Combine FROM AS" or "Combine AS FROM" !! */ 

	for (n=1; n<argc; n++)
		{
		if ( stricmp(argv.args[n],"FROM") == 0 )
			{
			if (from[0] == '\0')
				{
				if (++n < argc)
					{
					strcpy(from,argv.args[n]);
					}
				else Usage(NULL,ARG_ERR);
				}

			else if ((from[0]) && (as[0] == '\0') && fromflag)
				{
				if (++n < argc)
					{
					strcpy(as,from);
					strcpy(from,argv.args[n]);
					}
				else Usage(NULL,ARG_ERR);
				}

			else Usage(NULL, LST_ERR);
			}


		else if (( stricmp(argv.args[n],"AS") == 0 ) ||
				 ( stricmp(argv.args[n],"TO") == 0 ))
			{
			if (as[0] == '\0')
				{
				if (++n < argc)
					{
					strcpy(as,argv.args[n]);
					}
				else Usage(NULL,ARG_ERR);
				}

			else Usage(NULL, DST_ERR);
			}

		else if ( (stricmp(argv.args[n],"QUIET") == 0) ||
				(stricmp(argv.args[n],"Q") == 0) )
			quietflag = TRUE;

		else if ( (stricmp(argv.args[n],"DELETE") == 0) ||
				(stricmp(argv.args[n],"D") == 0) )
			deleteflag = TRUE;


		else if (*from == '\0')
			{
			fromflag = TRUE;
			strcpy(from,argv.args[n]);
			}

		else if (*as == '\0')
			strcpy(as,argv.args[n]);


		else Usage(NULL,ARG_ERR);
		}
	}



if ((strcmp(from,"?") == 0)  || (strcmp(as,"?") == 0))
	Usage(NULL,NO_ERR);

if ( (*from == '\0') || (*as == '\0') )
	Usage(NULL,ARG_ERR);

if (stricmp(from,as) == 0)
	Usage(NULL,SAME_ERR);


/* We now have the filenames of the list and the destination	*/

/* Get the full "as" pathname */
UNLESS ( lock = Lock(as, ACCESS_READ) )
	{
/* "as" file doesn't exist so create it, empty */
	if (asfp = fopen(as,"w"))
		{
		if (fclose(asfp))
			Usage(as,CLOSE_ERR);
		}
	else Usage(as, OPEN_ERR);

	UNLESS ( lock = Lock(as, ACCESS_READ) )
		Usage(as, FIND_ERR);	/* Should be there! */

	}

else SaveAS();


if (getpath(lock, as))
	{
	UnLock(lock);
	Usage(as, GETPATH_ERR);
	}
UnLock(lock);



/* Open "from" for read */
UNLESS(fromfp = fopen(from,"r"))
	Usage(from, OPEN_ERR);


/* Get the "from" path */
UNLESS ( lock = Lock(from, ACCESS_READ) )
	Usage(from, FIND_ERR);


workinglock = ParentDir(lock);
UnLock(lock);


CurrentDir(workinglock);




/* Then read in the files to our filelist */
/* If one (or more) is the same as "AS" file, copy it to a tempfile	*/
/* and use tempfile in the list.  Keep the tempfile and inform the	*/
/* user at end if not quiet.  Returncode would then be WARN.		*/
/* There would be a similar problem if listfile was same as "AS" but	*/
/* this isn't permitted.						*/

while (getfname(fromfp))
	{


/*
 There is a problem of directories here...
 "thisname" is relative to the list file (or absolute reference).
 "as" is relative to the original current directory (or absolute reference).

 What we have to do is to get their full pathnames.
 
*/ 

/* Get the full "thisname" pathname */
/* This also ensures the file exists	*/

	UNLESS ( lock = Lock(thisname, ACCESS_READ) )
		Usage(thisname, FIND_ERR);

	if (getpath(lock, thisname))
		{
		UnLock(lock);
		Usage(thisname, GETPATH_ERR);
		}
	UnLock(lock);


 	if (stricmp(thisname, as) == 0)
		{
		SaveAS();

		UNLESS (addflist(tname))
			Usage(NULL,MEM_ERR);
		}

	else UNLESS (addflist(thisname))
		Usage(NULL,MEM_ERR);

	if (feof(fromfp)) break;
	}


/* Now open destination for write */
UNLESS (asfp = fopen(as,"w"))
	Usage(as, OPEN_ERR);


/* Now append all the files */
fl = ((struct FileNode *)&FList)->next;

while (fl)
	{
	UNLESS (quietflag)
		fprintf(out, "Appending \"%s\"\n", fl->fname);

/* NOTE: freopen() close the existing file */
	UNLESS ( fromfp = freopen(fl->fname, "r", fromfp))
		{
		Usage(fl->fname,OPEN_ERR);
		}

	if (fappend(asfp, fromfp))
		Usage( fl->fname, CPYIO_ERR);

	fl = fl->next;
	}

/* ALL DONE */

cleanexit(RETURN_OK);
}




/* Returns NULL for failure */
struct FileNode * addflist(char *name)
{
struct FileNode *node;
struct FileNode *fl	= (struct FileNode *)(&FList);


/* Point to last node in the list */
while (fl->next)
	{
	fl = fl->next;
	}

/* Get a node, memory is cleaned automatically at termination with malloc */
if (node = malloc(sizeof(struct FileNode) + strlen(name) + 1))
	{
	fl->next = node;
	node->next = NULL;
	strcpy(&node->fname[0], name);
	}

return(node);
}





int fncopy(char *to, char *from)
{
FILE *tofp, *fromfp;
int rc;

if ( (tofp = fopen(to, "w")) && (fromfp = fopen(from, "r")) )
	{
	UNLESS (rc = fappend(tofp,fromfp))
		if ( (fclose(tofp)) || (fclose(fromfp)) )
			rc = -1;
	}

else rc = -1;

return(rc);
}



VOID SaveAS(VOID)
{
if (tname[0] == '\0')		/* i.e. if not already saved! */
	{
	if (tname == (tmpnam(tname)))
		{
		UNLESS (quietflag)
			fprintf(out, "Copying \"%s\" to \"%s\"\n", as, tname);

		UNLESS ( (fncopy(tname,as) == 0) )
			Usage(tname, CPYIO_ERR);
		}

	else Usage(as, TEMP_ERR);
	}
return;
}





int fappend(FILE *to, FILE *from)
{
int i;
char buffer[256];

/* Position to end of to file */
if (fseek(to, 0L, SEEK_END) != -1)
	{
	while (i = fread(buffer, 1, 256, from)) 
		if (fwrite(buffer, 1, i, to) != i)
			break;
	}

i = (ferror(to) || ferror(from)) ? -1 : 0;
return(i);
}



/* Skip rest of line */
int skipline(FILE *fp)
{
char buf;

while ( (fread(&buf, 1, 1, fp)) == 1 )
	if (buf == '\n')
		break;

return( (ferror(fp) ? -1 : 0) );
}



/* Return pointer to filename or NULL if no more */
/* Spaces can be used as separators but NOT commas */
/* Control characters are all treated as non-valid i.e. separators */

char *getfname(FILE *fp)
{
int n = 0;
BOOL quoteflag;

while ( (fread(&thisname[n],1,1,fp)) == 1 )
	{
	if ((thisname[n]) == ';')		/* This is a comment */
		{
		if (skipline(fp))
			Usage(NULL,READ_ERR);
		}

/* All normal characters except space are treated as valid */
	else if ( ((thisname[n] >= '\x21') && (thisname[n] <= '\x7e')) ||
			((thisname[n] >= '\xa1') && (thisname[n] <= '\xff')) )
		break;

	}

UNLESS ( (ferror(fp)) || (feof(fp)) )
	{
/* There should now be a valid character in buffer */
	UNLESS (quoteflag =  (*thisname == '\"') ? TRUE : FALSE)
		n++;
	while ( (fread(&thisname[n],1,1,fp)) == 1 )
		{

		if (quoteflag && (thisname[n] == '\"'))
			{
			break;
			}

		if (iscntrl(thisname[n]))
			break;
	
		if (isspace(thisname[n]) && (!quoteflag))
			break;

		n++;	/* was a valid character */
		}

	}

thisname[n] = '\0';

if (ferror(fp))
	Usage(NULL,READ_ERR);

return( ( (n) ? thisname : NULL) );

}





/****************************************
 * Get user's input from stdin...	*
 * Outputs to stdout.			*
 * NOTE:				*
 * It is VERY IMPORTANT to flush before	*
 * getch() if it is required to		*
 * recognize the CLOSEWINDOW gadget.	*
 ****************************************/

VOID getinput(UBYTE *buffer, int max)
{
int	n;		/* Index of last character in buffer	*/ 
int	cp;		/* Cursor position			*/

int	i;

UBYTE	ch0;
UBYTE	ch1;		/* for the CSI sequence */

n = cp = 0;

flushall();

HLBg_colour();	/* Highlight the background of the input */

while ( ((ch0 = getch()) != '\r') && (n < max) )
	{
	if (ch0 == CSI)
		{

		ch0 = getch();

		switch (ch0)
			{

/* Cursor movements, shifted cursor up/down, shifted tab */

			case CLEFT:

			if (cp)
				{
				printf("\b"); 	/* Back_cursor(); */
				fflush(stdout);
				cp--;
				}
			else scr_Beep();

			break;

			case CRIGHT:

			if (cp < n)
				{
				Forward_cursor();
				cp++;
				}
			else scr_Beep();

			break;



			case CUP:
			case CDOWN:
			case CSUP:
			case CSDOWN:
			case STAB:

			break;


/* Shifted Cursor right/left */

			case ' ':

			ch1 = getch();

			if (ch1==CSRIGHT)
				{
				if (cp<n)
					{
					while (cp<n)	
						{
						Forward_cursor();
						cp++;
						}
					}
				else scr_Beep();
				break;
				}

			if (ch1==CSLEFT)
				{
				if (cp)
					{
					while (cp)
						{
						printf("\b");
						fflush(stdout);
						cp--;
						}
					}
				else scr_Beep();

				break;
				}


/*
 ? window close or some other event. REMOVE IT!
 ----------------------------------------------
 It appears an input event can be of varying length BUT
 it always ends with '|'.  I originally thought it was 22 bytes
 plus an extra 10 bytes as a buffer making 32, but it doesn't work!
*/

			while (getch() != '|') ;

			break;


/* Function Keys */
			case F1:
			case F2:
			case F3:
			case F4:
			case F5:
			case F6:
			case F7:
			case F8:
			case F9:
			case F10:

			ch1 = getch();
			
			if (ch1 == '~')
				break;		/* was a function key */

/* Shifted function keys */

			if ( (ch0 == '1') &&
				(ch1>=F1) && (ch1<=F10) )
				{
				if (getch() == '~')
					break;	/* was a normal shifted F key */
				}

/* 101 function keys (20 21 normal, 30 31 shifted */

			if ( ((ch0 == '2') || (ch0 == '3')) &&
				((ch1 == '0') || (ch1 == '1')) )

				{
				if (getch() == '~')
					break;	/* was a 101 function key */
				}

/* 101 keys, Insert, PageUp, PageDown, Pause/Break, Home, End (4) shifted (5) */ 

			if ( ((ch0 == '4') || (ch0 == '5')) &&
				((ch1 >= '0') && (ch1 <= '5')) )

				{
				if (getch() == '~')
					break;	/* was a 101 special key */
				}

/*
 ? window close or some other event. REMOVE IT!
 ----------------------------------------------
*/
			while (getch() != '|') ;
			break;


			case HELP:

			if (getch() == '~')
				break;	/* was HELP key */



/*                ****** FALL THROUGH ******

 ? window close or some other event. REMOVE IT!
 ----------------------------------------------
*/

			default:

			while (getch() != '|') ;
			break;
			
			}

		}

/* These are the valid characters.  This permits all the alternative chars */

	else if ( ((ch0 >= '\x20') && (ch0 <= '\x7e')) ||
				((ch0 >= '\xa1') && (ch0 <= '\xff')) )
		{
		if (n < (max-1)) /* allow 1 byte for zero termination */
			{
			for (i=n; i>cp; i--)
				buffer[i] = buffer[i-1];
			n++;	
			buffer[cp] = ch0;
			for (i=cp; i<n; i++)
				printf("%c",buffer[i]);
			cp++;
			for (i=n; i>cp; i--)
				printf("\b"); 		/* Back_cursor(); */

			fflush(stdout);
			}
		else scr_Beep();
		}


	else switch (ch0)
		{

/* BACKSPACE delete */

		case '\b':

		if (cp)
			{
			cp--;
			printf("\b"); 		/* Back_cursor(); */
			}
		else
			{
			 scr_Beep();
			 break;
			 }

/* fall through into DEL... */

		case DEL:

		if (cp < n)

/* DELETE key: delete character under cursor */
			{
			for (i=cp; i<n-1; i++)
				buffer[i] = buffer[i+1];

			for (i=cp; i<n-1; i++)
				printf("%c",buffer[i]);

			fflush(stdout);
			NORMAL_colour();	/* Return colours to normal */
			printf(" ");		/* Remove last screen character */
			HLBg_colour();

			for (i=n; i>cp; i--)
				printf("\b"); 		/* Back_cursor(); */
			n--;
			}

		else scr_Beep();

		break;

		default:
		scr_Beep();
		break;
		}
	}

buffer[n] = '\0';


printf("\n");

NORMAL_colour();	/* Return colours to normal */
return;
}




/* Fix up our own cleanup code for Ctrl C and Ctrl D */

void __regargs _CXBRK(void)
{
UNLESS (quietflag)
	{
	if (wbflag) NORMAL_colour();	/* In case we were at input routine */

	fprintf(out, "\n**USER ABORT**\n");
	}

cleanexit(RETURN_OK);
}



/* If there has been an error */

void Usage(char *fn, int n)
{
UNLESS (quietflag)
	{
if (fn) fprintf(out, "\nFile \"%s\": ",fn);
if (n) fprintf(out, "\nERROR: %s\n",AppErrors[n]);
								
if (n < OPEN_ERR)
fprintf(out, 
  "USAGE:  COMBINE [FROM] <listname> [AS/TO] <destination> [DELETE] [QUIET]\n");
	}

cleanexit( ((n==NO_ERR) ? RETURN_OK : RETURN_ERROR) );
}







/* Quit routine */

void cleanexit(int rc)
{

CurrentDir(oldlock);	/* return to original directory */

if (workinglock != -1L)
	UnLock(workinglock);	/* free the working lock */



if (tname[0])
	{
	if ((rc == FALSE) && deleteflag)
		{
		UNLESS (quietflag)
				fprintf(out, "Deleting \"%s\"\n",tname);

		UNLESS (DeleteFile(tname))
			{
			rc = RETURN_WARN;
			UNLESS (quietflag)
				fprintf(out, "ERROR: \"%s\" not deleted!\n", tname);
			}
		}
	else
		{
/* If there is already a return code it stays the same */
		UNLESS (rc) rc = RETURN_WARN;
		UNLESS (quietflag)
			fprintf(out, "Destination saved as \"%s\"\n",tname);
		}
	}


/* Can't use quietflag here */

if (wbflag)
	{

/* What I really want is press a key OR windowclose...	*/
/* Have to keep all files flushed, especially stdout.	*/
	printf("\nPress ");

	if (vflag) printf("WINDOWCLOSE Gadget or "); 

	printf("a key to exit: \n");
	flushall();
	getch();
	}



fcloseall();	/* Close all open files */

/*
 Allow the C exit code to clear up memory....
 Not quite right but why duplicate the code?
*/

exit(rc);
}


