
#include	<portab.h>
#include	"cpmfunc.h"

EXTERN	WORD	_EXTERR;		/* Has AX after an O.S. call	*/

#define		DMA_LEN		128
#define		FCB_LEN		36

#define		REC_LEN		23
#define		REC_DRIVE	0
#define		REC_FILE	1
#define		REC_TYPE	9
#define		REC_PASSWORD	12
#define		REC_FIELDS	20
#define		REC_8087	20
#define		REC_SHARED	21
#define		REC_SUSPEND	22
#define		REC_F_8087	0
#define		REC_F_SHARED	1
#define		REC_F_SUSPEND	2


#define		NOT_FOUND	-1
#define		LOW_TO_UP	('a' - 'A')
#define		EXIT_PROGRAM	-1
#define		CONTINUE	1

#define		BDOS_MASK	0x00ff
#define		OS_MASK		0xff00
#define		PASSWORD	0x07ff
#define		RO_ERROR	0x03ff
#define		RO_OR_PASS	0xfe

#define		BDOS_VER	0x0031
#define		OS_TYPE		0x1400

/***	FUNCTIONS THAT RETURN A POINTER    ***/

BYTE	*a_white_char();
BYTE	*check_format();
BYTE	*expand_file_name();
BYTE	*get_token();
BYTE	*search();
BYTE	*short_string();
BYTE	*getspass();

struct	_fcblst	*expfcb();
/**BYTE	*getpass();**/
BYTE	*strncpy();
BYTE	*index();
BYTE	*strncmp();
BYTE	*strcmp();
/*	strlen(); */


/***	ERROR MESSAGES    ***/

BYTE	*err_00_msg  =  "REQUIRES CONCURRENT CP/M-86 3.1$";
BYTE	*err_01_msg  =  "Invalid syntax -- one filespec only";
BYTE	*err_02_msg  =  "Use CMD or blank filetype in CHSET command line";
BYTE	*err_03_msg  =  "Invalid filespec";	/* too long */
BYTE	*err_04_msg  =  "Invalid filespec";	/* can't add .cmd */
BYTE	*err_05_msg  =  "Invalid syntax -- expected a '['";
BYTE	*err_06_msg  =  "Not a valid CHSET field";
BYTE	*err_07_msg  =  "Not a valid CHSET setting";
BYTE	*err_08_msg  =  "Duplicate field";
BYTE	*err_09_msg  =  "Invalid syntax -- expected a '='";
BYTE	*err_10_msg  =  "Invalid syntax -- expected ',' or ']'";
BYTE	*err_11_msg  =  "Wildcards are not allowed when using CHSET to change a setting";
BYTE	*err_12_msg  =  "Invalid filespec";
BYTE	*err_13_msg  =  "File not found";
BYTE	*err_14_msg  =  "Too many directory entries for query";



BYTE	*pass_prompt  =  "  Password? ";
BYTE	*set_to	      =  " set to ";
BYTE	*setting_is   =  " settings are ";
BYTE	*blanks	      =  "          ";		/* 10 blanks 		*/


/***	GLOBAL VARIABLES    ***/
	
BYTE	buff_01[DMA_LEN];	/* Buffer to hold current token		*/
BYTE	*token;			/* Points to buffer			*/

BYTE	buff_02[24];
BYTE	*file_spec;

BYTE	buff_03[FCB_LEN];
BYTE	*fcb;


BYTE	*cmd		=	".CMD" 					;
BYTE	*deli_01	=	"\t\\\" !@#$%^&*()_+{}~:|<>?-=[]`;',./" ;
BYTE	*deli_02	=	"\t =<>,|[]"				;


	struct	_fcblst
	{
		BYTE		fcb[FCB_LEN];
		struct _fcblst	*next_fcb;
	};

	struct  pfcb_str
	{
		BYTE	*filename;
		BYTE	*fcb_adr;
	};

	struct	abcd_str
	{
		WORD	start;
		WORD	end;
	};

	struct	fie1_str
	{
	   	BYTE	*fieldstr;
		WORD	field_nu;
	};

#define		EMPTY		-1	/* Can not be same as option number */
#define		FIE1_START	0
#define 	FIE1_END	2

	struct	fie1_str	fie1_tab[] =
	{
		"8*087",0,
		"SH*ARED",1,
		"SU*SPEND",2
	};

	struct	fie1_str	opt1_tab[] =
	{
		"ON*",0,
		"OF*F",1,		
		"OP*TIONAL",2
	};

	struct	abcd_str	xref_tab[] =
	{
		0,2,		/* on, off, optional			*/
		0,1,		/* on, off				*/
		0,1		/* on, off				*/
	};

	


VOID	main()

{
	BYTE				*dma;
	WORD				dma_len;


	if ( (ok_ver(BDOS_VER,OS_TYPE) == FALSE) )
	{			/* Not the right system			*/
	    c_writestr(err_00_msg);		/* delimited with $	*/
	    p_termcpm();			/* Terminate		*/
	}
	token = &buff_01[0];
	file_spec = &buff_02[0];
	fcb = &buff_03[0];
	f_errmode( 0xfe );	/* Sets BDOS errors to verbose 		*/
	c_delimit( NULL );	/* Sets output delimiter to NULL	*/
	dma = f_dmaget();	/* Get DMA ptr				*/
	dma_len = *dma++;	/* Get length, move ptr to str portion	*/
	*(dma+dma_len) = NULL;
	upper_case(dma);	/* Change all lowercase to upper	*/
	if ( (dma_len == 0) )
	{			/* Have no tail				*/
	    display_help();
	}
      else
	{
	    dma = a_white_char(dma);	/* DMA now points to a white char */
	    if ( (*dma == '[') )
	    {			/* Give help on [...			*/
		display_help();
	    }
	  else
	    {
		set_or_show(dma);	/* Does return on success 	*/
	    }
	}
}	/*** MAIN ***/

VOID	show_mode(string,token,file_spec)

	BYTE			*string;
	BYTE			*token;
	BYTE			*file_spec;

	{

	 WORD			error;
	 BYTE			buffer[REC_LEN];	/* record buffer */
	 BYTE			*record;
	 BYTE			*pass;	/* pts to password from C RTL	*/
	 WORD			first;	/* flag for search first or next */
	 struct	_fcblst		*files;	/* linklist of file names	*/


	    if ( (*token == NULL) )
	    {		/* user type 'chset $...' so token will be null	*/
		print_err(err_04_msg,EXIT_PROGRAM);
	    }
	    record = &buffer[0];
	    string = a_white_char(string);
	    if ( (*string != NULL) )
	    {		/* extra characters on line ???two filespecs???	*/
		print_err(err_01_msg,EXIT_PROGRAM);
	    }
	    files = expfcb(file_spec);	/* Returns linklist		*/
	    while ( (files != NULL) )
	    {
		copy(files->fcb,record,20);
		error = get_settings(record);
		if ( error == PASSWORD )
		{
		    passget(record);
		    error = get_settings(record);
		}
		if ( (error == 0) )
		{
		    print(record,setting_is);
		}
	      else
		{		/* extra line after bdos error		*/
		    c_writestr("\n\r");
		}
		files = files->next_fcb;
	    }
	}



VOID	set_mode(string,token,file_spec)

	BYTE			*string;
	BYTE			*token;
	BYTE			*file_spec;

	{

	 BYTE			buffer[REC_LEN];	/* record buffer  */
	 BYTE			*record;
	 BYTE			i;
	 WORD			error;
	 BYTE			*pass;	/* pts to password from C RTL	*/
	 struct	_fcblst		*files;	/* linklist of files		*/


	    record = &buffer[0];
	    if ( (index(file_spec,'?') == NULL) &&
		 (index(file_spec,'*') == NULL) )
	    {			/* O.K. no wild cards are present 	*/
	        string = a_white_char(string);	/* Move to a white char	*/
	        if ( (*string == '[') )
	        {			/* O.K. start symbol 		*/
		    string = check_format(string,token,record);
		    string = a_white_char(string);	/* White char	*/
		    if ( (*string == NULL) || (*string == ']') )
		    {		/* COMMAND OPTIONS HAVE BEEN ACCEPTED 	*/
			files = expfcb(file_spec);	/* Linklist	*/
			/* FCB has dfffffffftttpppppppp			*/
			for ( i=0; i<20; i++)
			{
			    *(record+i) = *(files->fcb+i);
			}
			error = set_fields(record);
			if ( error == PASSWORD )
			{
			    passget(record);
			    error = set_fields(record);
			}
			if ( error == 0)
			{
			    print(record,set_to);
			}
		      else
			{		/* extra line after bdos error		*/
			    c_writestr("\n\r");
			}
		    }
		  else
		    {  
		        print_err(err_10_msg,EXIT_PROGRAM);
		    }
	        }
	       else
	        {			/* Illegal start symbol		*/
		    print_err(err_05_msg,EXIT_PROGRAM);
	        }
	    }
	  else
	    {				/* No wild cards are allowed	*/
		print_err(err_11_msg,EXIT_PROGRAM);
	    }
	}

BYTE	*check_format(string,token,record)

	BYTE			*string;
	BYTE			*token;
	BYTE			*record;	/* Record to set fields in */

	{

	 WORD			field;
	 WORD			option;
	 WORD			start;
	 WORD			end;

	    *(record+REC_8087) = EMPTY;		/* 8087 field		*/
	    *(record+REC_SHARED) = EMPTY;   /* SHARED field of record	*/
	    *(record+REC_SUSPEND) = EMPTY;  /* SUSPEND field of record	*/
	    string++;
	    FOREVER		/* Exits on an error or success		*/
	    {
/*****			string++;   	****/
		string = get_token(string,deli_01,token);
		field = fisttonu(token,fie1_tab,FIE1_START,FIE1_END);
		if ( (field != NOT_FOUND) )
		{				/* O.K. field		*/
		    string = a_white_char(string);
		    if ( (*string++ == '=') )
		    {				/* O.K. seperator	*/
			string = get_token(string,deli_01,token);
			start = xref_tab[field].start;
			end = xref_tab[field].end;
			option = fisttonu(token,opt1_tab,start,end);
			if ( (option != NOT_FOUND) )
			{	/* Have 'field=option' check and record */
			    if (((*(record+REC_FIELDS+field)) == ((BYTE)EMPTY)))
			    {		/* EVERTHING O.K. on this pass */
				*(record+REC_FIELDS+field) = option;
				string = a_white_char(string);
				if ( (*string == ']') || (*string == '\0') )
	/**************     if ( (*string != ',') ) ***********/
				{	/* No more 'field=option' */
				    return(string);
				}
			      else
				{	/* accept ',' as well as ' '	*/
				    if (*string == ',') string++;
				}
			    }
			  else
			    {		/* Duplication error	*/
				print_err(err_08_msg,EXIT_PROGRAM);
			    }
			}
		      else
			{
			    print_err(err_07_msg,EXIT_PROGRAM);
			}
		    }
		  else
		    {				/* Expected an '='	*/
			print_err(err_09_msg,EXIT_PROGRAM);
		    }
		}
	      else
		{			/* Token is not legal field 	*/
		    print_err(err_06_msg,EXIT_PROGRAM);
		}
	    }
	}


VOID	set_or_show(string,file_spec)

	BYTE			*string;
	BYTE			*file_spec;

	{

	    string = get_token(string,deli_02,token);
	    add_cmd(token,file_spec);
	    if ( (index(string,'[') == NULL) )	/* i.e. did not find it	*/
	    {		/* User wishes to set */
		show_mode(string,token,file_spec);
	    }
	     else
	    {		/* User wishes to show		*/
		set_mode(string,token,file_spec);
	    }
	}


BYTE	*get_token(string,delimiters,token)

	BYTE			*string;
	BYTE			*delimiters;
	BYTE			*token;

	{

	 BYTE			*index;

	    string = a_white_char(string);
	    while ( (*string != NULL) )
	    {
		for ( index=delimiters; *index != NULL; *index++ )
		{
		    if (*index == *string)
		    {		/* Found a delimiter, prepare to return	*/
			*token = NULL;
			return(string);
		    }
		}
		*token++ = *string++;
	    }
	    *token = NULL;
	    return(token);
	}



VOID	display_help()

	{

	    c_writestr("Syntax:\n\r\n\r");
	    c_writestr("    CHSET {d:}filename{.CMD}\n\r");
	    c_writestr("    CHSET {d:}filename{.CMD} [field=setting{,field=setting...}]\n\r");
	    c_writestr("    CHSET [HELP]\n\r\n\r");
	    c_writestr("Fields and Settings:\n\r\n\r");
	    c_writestr("    8087        ON or OPT or OFF\n\r");
	    c_writestr("    SHARED      ON or OFF \n\r");
	    c_writestr("    SUSPEND     ON or OFF \n\r\n\r");
	    c_writestr("Examples:\n\r\n\r");
	    c_writestr("    CHSET qwe.cmd [shared=on]          ; Sets the shared field of qwe.cmd\n\r");
	    c_writestr("    CHSET editor [sus=on]              ; Sets editor.cmd to suspend\n\r");
	    c_writestr("    CHSET pie [80=opt]                 ; Sets the 8087 field to optional\n\r");
	    c_writestr("    CHSET *                            ; Displays settings for all CMD files\n\r");
	    c_writestr("    CHSET qwerty                       ; Display current settings of qwerty.cmd\n\r");
/***
	    c_writestr("    CHSET calc.cmd [8=on,su=off,sh=on]\n\r");
	    c_writestr("    CHSET calc1 [8=off,su=on,sh=off]\n\r");
	    c_writestr("    CHSET a*\n\r");
***/
	}




VOID	add_cmd(token,file_spec)

	BYTE			*token;		/* current file_spec */
	BYTE			*file_spec;

	{
	 BYTE			*found;
	 BYTE			*spec_index = file_spec;

	    if ( (strlen(token) < 24) )
	    {
	        found = search(".",token);
		if ( (*found == NULL) )
		{			/* We must add '.CMD' */
		    if ( (strlen(token) < 20) )
		    {
 		        found = search(";",token);	/* found pts to NULL or ';' */
		        while ( (*token != *found) )
		        {
		            *spec_index++ = *token++;
		        }
		        found = strncpy(spec_index,cmd,4);
		        spec_index = spec_index + 4;
		        while ( (*token != NULL) )
		        {
		            *spec_index++ = *token++;
		        }
		        *spec_index = NULL;
	            }
	          else
		    {		/* File spec to long to add '.cmd'	*/
			print_err(err_04_msg,EXIT_PROGRAM);
		    }
		}
	      else
		{		/* User has specified extension		*/
		    found = strncmp(found,cmd,4);	/* Is ext '.CMD' */
		    if ( (found == NULL) )
		    {				/* Extension is 'CMD'	*/
		        strncpy(file_spec,token,24);
		    }
		  else
		    {				/* Illegal extension	*/
		        print_err(err_02_msg,EXIT_PROGRAM);
		    }
	        }
	    }
	   else
	    {		/* File spec is too long to be in correct form	*/
		print_err(err_03_msg,EXIT_PROGRAM);
	    }
	}


VOID	print_err(string,mode)

	BYTE			*string;
	WORD			mode;

	{

	    c_writestr(string);
	    c_writestr("\n\r");
	    if ( (mode == EXIT_PROGRAM) )
	    {
		p_termcpm();
	    }
	}


BYTE	*search(sub_string,string)

/******

    OUTPUT
	returns ptr to first character of match or ptr to NULL if no match.

    RESTRICTIONS
	Sub_string is defined as 1+ characters ending with a NULL.

*******/

	BYTE			*sub_string;	/* String searching for	*/
	BYTE			*string;	/* String looking in	*/

	{

	 BYTE			*tmp1;
	 BYTE			*tmp2;

	    while ( (*string != NULL) )
	    {
		tmp1 = sub_string;
		tmp2 = string;
		while ( (*tmp1++ == *tmp2++) )
		{
		    if ( (*tmp1 == NULL) )
		    {				/* Found a match	*/
			return(string);
		    }
		    if ( (*tmp2 == NULL) )
		    {		/* Tmp1 is longer so no match possible	*/
			return(tmp2);
		    }
		}
		string++;
	    }
	    return(string);
	}


VOID	upper_case(string)

	BYTE			*string;

	{

	    while ( ((*string) != NULL) )
	    {
		if ( ('a' <=  (*string)) && ((*string) <= 'z') )
		{
		    *string = (*string) - LOW_TO_UP;
		}
		string++;
	    }
	}


WORD	fisttonu(string,table,start,end)

/****
        Will return NOT_FOUND if given a NULL string
****/

	BYTE			*string;
	struct fie1_str		table[];
	WORD			start;
	WORD			end;

	{
	 struct fie1_str	*loop	=   &table[start];
	 WORD			cnt	=   start;

	    if ( (*string == NULL) )
	    {			/* Special case		*/
		return(NOT_FOUND);
	    }
       	    while ( (cnt <= end) )
	    {
	        if ( (abrmatch(string,loop->fieldstr)) > -10 )
	        {
	            return(loop->field_nu);
	        }
	      else
	        {
	            cnt++;
	            loop++;
	        }
	    }
	    return(NOT_FOUND);
	}


WORD	abrmatch(word1,word2)

	BYTE				*word1;
	BYTE				*word2;

	{
	WORD				state	=   -30;

	    while ( (*word2 != NULL) )
	    {
	        if ( (*word2 == '*') )
	        {
	            if ( (*word1 == NULL) )
		    {
			if ( (*++word2 == NULL) )
			{
			    return(20);
			}
		      else
			{
			    return(10);
			}
		    }
		    *word2++;
		    state = -10;
	        }
	      else
	        {
	            if ( (*word1 != *word2) )
	            {
			if ( (*word1 == NULL) && (state == -10) )
			{
			    return(0);
			}
		      else
			{
			    return(state);
			}
	            }
		  else
		    {
			*word1++;
			*word2++;
		    }
	        }
	    }
		/*** at end of word2 ***/
	    if ( (*word1 == *word2) )
	    {	/*** at end of word1 also ***/
	        return(20);	/* perfect match */
	    }
	  else
	    {	/*** word1 has more letters left ***/
		return(-20);
	    }
	}


BYTE	*a_white_char(string)

	BYTE			*string;

	{

	    while ( (*string == ' ') || (*string == '\t') )
	    {
		string++;
	    }
	    return(string);
	}

VOID	print(record,string)

	BYTE			*record;
	BYTE			*string;

	{

	 BYTE			print_buffer[80];
	 BYTE			*prt_buf;
	 WORD			index;
	 WORD			i;

	    prt_buf = &print_buffer[0];
	    prt_buf = expand_file_name(record,prt_buf);
	    strcpy(prt_buf,string);	/* Gets string			*/
	    prt_buf = prt_buf + strlen(string);
	/*** Now have D:FILENAME.TYP'string' ***/
	    *prt_buf++ = '[';
	    record = record + REC_FIELDS;	/* Pts to fields	*/
	    for ( i = FIE1_START; i <= FIE1_END; i++,record++)
	    {
		if ( (*record != (BYTE)EMPTY) )
		{	/* Only print if field is not empty		*/
		    prt_buf = short_string(prt_buf,fie1_tab[i].fieldstr);
		    *prt_buf++ = '=';
		    index = xref_tab[i].start + (*record);
		    prt_buf = short_string(prt_buf,opt1_tab[index].fieldstr);
		    *prt_buf++ = ',';
		}
	    }
	    *(--prt_buf) = ']';		/* write over last ','		*/
	    *(++prt_buf) = '\n';
	    *(++prt_buf) = '\r';
	    *(++prt_buf) = NULL;
	    c_writestr(&print_buffer[0]);
	}

BYTE	*short_string(buffer,string)

	BYTE			*buffer;	/* Destination		*/
	BYTE			*string;	/* Source string	*/

/***	

	Copies all characters of a string except '*' and the NULL
    terminator to a buffer.

	OUTPUT
		A ptr to the location one past the last copied
	    character in the buffer.

***/

	{

	    while ( *string != NULL )
	    {
		if ( *string != '*')
		{		/* Copy character			*/
		    *buffer++ = *string++;
		}
	      else
		{
		    string++;
		}
	    }
	     return(buffer);
	}


/*----------------------------------------------------------------------*\
 |	NAME	:  expfcb						|
 |	CREATED	:  10-August-83		LAST MODIFIED:  12-September-83 |
 |	FUNCTION:  Expfcb expands the input filespec into a list of	|
 |		   FCB's						|
 |	INPUT	:  filespec -- ptr to filespec given on command line	|
 |	OUTPUT	:  Returns ptr to list of FCB's created from filespec	|
\*----------------------------------------------------------------------*/

struct	_fcblst   *expfcb( filespec )
	BYTE		*filespec;
{
/****	BYTE		fcb[FCB_LEN];	/* file control block buffer	*/
	BYTE		dma[DMA_LEN];	/* DMA buffer			*/
	WORD		dindex;		/* DMA buffer index		*/
	WORD		findex;		/* FCB buffer index		*/
	WORD		fcount;		/* file count			*/
	BYTE		*save_dma;	/* DMA offset save area		*/
	struct		_fcblst  *fhead;/* fcb list head ptr		*/
	struct		_fcblst  *ftail;/* fcb list tail ptr		*/
	struct		_fcblst  *flist;/* fcb	list			*/
/****	struct		_pfcb	  pfcb;	/* parse FCB for F_PARSE	*/
	WORD			error_code;
	struct	pfcb_str	pfcb;

	    save_dma = f_dmaget();
	    pfcb.filename = filespec;
	    pfcb.fcb_adr = fcb;
	    error_code = f_parse(&pfcb);
	    if ( (error_code != FALSE) )
	    {				/* We got an error		*/
		print_err(err_12_msg,EXIT_PROGRAM);
	    }
	    fcb[12] = 0;			/* extent number	*/
	fcount = 0;
	fhead = malloc( sizeof( struct _fcblst ) );
	if (fhead == 0)
	{			/* Out of room	*/
	     print_err(err_14_msg,EXIT_PROGRAM);
	}
	f_dmaset( dma );
	dindex = f_sfirst( fcb );
	if ( dindex == 0x00ff )
	{
	    print_err(err_13_msg,EXIT_PROGRAM);
	}
	while( dindex != 0x00ff )
	{
	   dindex = (dindex << 5) + 1;		/* dindex * 32, skip	*/
	   fcount++;				/* user number field	*/ 
	   flist = malloc( sizeof( struct _fcblst ) );
	    if (flist == 0)
	    {			/* Out of room	*/
	         print_err(err_14_msg,EXIT_PROGRAM);
	    }
	   fhead->next_fcb = flist;
	   fhead = flist;
	   if( fcount == 1 )
	      ftail = flist;
	   flist->fcb[0] = fcb[0];		/* insert drive code	*/
	   for( findex=1; findex < FCB_LEN; findex++ )	/* copy FCB	*/
	      flist->fcb[findex] = dma[dindex++];
	   for( findex=16; findex < 24; findex++ )	/* copy password*/
	      flist->fcb[findex-4] = fcb[findex];
     	   dindex = f_snext( fcb );
	}
	flist->next_fcb = NULL;			/* end FCB list		*/
	f_dmaset( save_dma );			/* reset DMA address	*/
	return( ftail );			/* front of list	*/
}


VOID	passget(record)

	BYTE			*record;

	{

	 BYTE			*pass;
	 BYTE			buffer[15];
	 BYTE			*prt_buf;

	    c_writestr("\n\r");
	    prt_buf = &buffer[0];
	    prt_buf = expand_file_name(record,prt_buf);
	    *prt_buf = NULL;
	    c_writestr(&buffer[0]);
	    pass = getspass(pass_prompt);	/* C RTL routine	*/
	    c_writestr("\n\r");
	    upper_case(pass);		/* Convert to upper case	*/
	    strncpy(record+12,blanks,8);		/* Blank out	*/
	    strncpy(record+12,pass,strlen(pass));	/* Replace	*/
	}

/*----------------------------------------------------------------------*\
 |	NAME	:  ok_ver						|
 |	CREATED	:  5-August-83		LAST MODIFIED:  12-September-83 |
 |	FUNCTION:  Ok_ver checks to see that the correct BDOS and OS	|
 |		   are being used.					|
 |	INPUT	:  bdos_ver -- BDOS version number to look for.		|
 |		   os_ver   -- OS version number to look for.		|
 |	OUTPUT	:  Returns 1 for true, 0 for false.			|
\*----------------------------------------------------------------------*/

WORD	ok_ver( bdos_ver,os_ver )
	WORD			bdos_ver;	/* min. BDOS version	*/
	WORD			os_ver;		/* min. CP/M version	*/
{
	WORD	ver;		/* S_BDOSVER return value (version #)	*/

	s_bdosver();
	ver = _EXTERR;
	if(((ver & BDOS_MASK) < bdos_ver) || ((ver & OS_MASK) != os_ver))
	   return(FALSE);
	return(TRUE);
}

BYTE	*getspass(prompt)

	BYTE			*prompt;

	{

	 BYTE			pasword[8];
	 WORD			pindex,xindex,ch;

	    c_writestr(prompt);
	    for (xindex=0;xindex<8;xindex++)
	    {
		pasword[xindex] = ' ';
	    }
	    pindex = 0;
	    while (pindex<8)
	    {
		ch = c_rawio(0x00fd);		/* read a character	*/
		if ( ch > ' ')
		{
		    pasword[pindex++] = (BYTE)ch;
		}
	      else
		{
		    switch(ch)
		    {
			case '\003' :	p_termcpm();		/* ^C */
					break;
			case '\010' :	if (pindex > 0 )	/* backspace */
					    pasword[--pindex] = ' ';
					break;
			case '\015' :	c_writestr("\n\r");	/* CR */
					pindex = 8;
					break;
			case '\030' :	for( xindex=0;xindex<8;xindex++) /* ^X */
					    pasword[xindex] = ' ';
					pindex = 0;
					break;
			default     :	break;
		    }
		}
	    }
	    ch = c_stat();
	    return(&pasword[0]);
	}


BYTE	*expand_file_name(record,buffer)

	BYTE				*record;
	BYTE				*buffer;

	{
	 BYTE				*prt_buf;
	 WORD				i;

	    prt_buf = buffer;		/* pts to buffer to store name	*/
	    if (*record != 0)
	    {			/* not the default drive		*/
		*prt_buf++ = (*record) + 64;	/* store letter		*/
		*prt_buf++ = ':';
	    }
	    record++;		/* Move record ptr to filename		*/
	    for ( i=0; i<8 ; prt_buf++,i++,record++ )
	    {			/*  Get filename  7-bit  ASCII		*/
		*prt_buf = *record & 0x7f;
	    }
	    *prt_buf++ = '.';
	    for ( i=0; i<3 ; prt_buf++,i++,record++ )
	    {			/* Get type  7-bits ASCII		*/
		*prt_buf = *record & 0x7f;
	    }
	    return(prt_buf);
	}

		
VOID	patch_area()

	{

	 WORD			i;

	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;
	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;
	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;
	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;
	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;
	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;
	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;
	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;
	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;
	    i=i+5; i=i+5; i=i+5; i=i+5; i=i+5;

	}
