/*****************************************************************************

	BldStr()

	This function "builds" a string.  This means converting string
build constructs in the input string into their intended equivalents
in the output string.  The string build constructs are as follows:

	^Q	use next character literally,  not as a string build char
	^R	use next character literally,  not as a string build char
	^V	lowercase the next character
	^V^V	lowercase all following characters
	^W	uppercase the next character
	^W^W	uppercase all following characters
	^EQq	use string in q-register q here
	^EUq	use ASCII char for number in q-register q here

	When this function is called,  CBfPtr points to the first character
of the input string.  It is assumed that the string is terminated by an ESCAPE
character (or something else if the calling command was @-modified).  If the
string is not properly terminated this function will die with "unterminated
command" when it encounters CStEnd while looking for the terminator character.

	When this function returns,  CBfPtr points to the ESCAPE which
terminates the string, the built string is in the buffer pointed to by
XBfBeg,  and XBfPtr points to the character after the last character in
the built string.

	The commands which contain a filename (EB, EI, EN, ER and
EW) use this function.  The EG command, which exits with an operating system
command line, uses this function.  The O command, which jumps to a tag, uses
this function.  The search commands (E_, FK, FN, FS, F_, N, S and _) use this
function.

*****************************************************************************/

#include "zport.h"		/* define portability identifiers */
#include "tecoc.h"		/* define general identifiers */
#include "defext.h"		/* define external global variables */
#include "deferr.h"		/* define identifiers for error messages */
#include "dchars.h"		/* define identifiers for characters */
#include "chmacs.h"		/* define character processing macros */

#if USE_PROTOTYPES
static DEFAULT DoCtVW(charptr EndArg, unsigned char TmpChr);
static DEFAULT DoCtE(charptr EndArg, charptr XBfEnd);
#endif

static charptr BBfPtr;			/* pointer into XBf */
static int CaseCv;			/* case conversion */
static unsigned char WVFlag;		/* ^W or ^V flag */

static DEFAULT DoCtVW(EndArg, TmpChr)	/* do a control-V or control-W */
charptr EndArg;				/* ptr to end of string argument */
unsigned char TmpChr;			/* temporary character */
{
    DBGFEN(3,"DoCtVW",NULL);

    WVFlag = TmpChr;
    if (++CBfPtr == EndArg) {		/* move past ^W or ^V,  too far? */
	ErrMsg(ERR_ISS);		/* yes, illegal search string */
	DBGFEX(2,DbgFNm,"FAILURE");
	return FAILURE;
    }
    if ((*CBfPtr == '^') && ((EdFlag & ED_CARET_OK) == 0)) {
	if (++CBfPtr == EndArg) {
	    ErrMsg(ERR_ISS);
	    DBGFEX(2,DbgFNm,"FAILURE");
	    return FAILURE;
	}
	TmpChr = To_Upper(*CBfPtr);
	if ((TmpChr < '@') || (TmpChr > '_')) {
	    ErrChr(ERR_IUC, *CBfPtr);
	    DBGFEX(2,DbgFNm,"FAILURE");
	    return FAILURE;
	}
	TmpChr &= '\077';
    } else {
	TmpChr = *CBfPtr;
    }
    if (WVFlag == CTRL_V) {
	if (TmpChr == CTRL_V) {
	    CaseCv = LOWER;
	} else {
	    *BBfPtr++ = To_Lower(TmpChr);
	}
    } else {
	if (TmpChr == CTRL_W) {
	    CaseCv = UPPER;
	} else {
	    *BBfPtr++ = To_Upper(TmpChr);
	}
    }
    WVFlag = '\0';
    DBGFEX(2,DbgFNm,"SUCCESS");
    return SUCCESS;
}


static DEFAULT DoCtE(EndArg, XBfEnd)	/* do a control-E */
charptr EndArg;				/* ptr to end of string argument */
charptr XBfEnd;				/* end of build-string buffer */
{
    DEFAULT	Status;			/* returned from FindQR */
    charptr	TCStEn;			/* temporary holder of CStEnd */

    DBGFEN(3,"DoCtE",NULL);
    if (++CBfPtr == EndArg) {		/* move past ^E,  too far? */
	ErrMsg(ERR_ICE);		/* yes, illegal ^E command */
	DBGFEX(2,DbgFNm,"FAILURE");
	return FAILURE;
    }
    if ((*CBfPtr == 'Q') || (*CBfPtr == 'q')) {
	if (++CBfPtr == EndArg) {
	    ErrMsg(ERR_ISS);
	    DBGFEX(2,DbgFNm,"FAILURE");
	    return FAILURE;
	}

/*
 * handle filespec buffer and search string buffer
 */
	if (*CBfPtr=='*' || *CBfPtr=='_') {
	    charptr  BufPtr, BufBeg;
	    if (*CBfPtr=='*') {
		BufPtr=FBfPtr;
		BufBeg=FBfBeg;
	    } else {
		BufPtr=SBfPtr;
		BufBeg=SBfBeg;
	    }
	    if ((BufPtr-BufBeg) > (XBfEnd-BBfPtr)) {
		ErrMsg(ERR_STL);
		DBGFEX(2,DbgFNm,"FAILURE");
		return FAILURE;
	    }
	    MEMMOVE(BBfPtr, BufBeg, (SIZE_T)(BufPtr - BufBeg));
	    BBfPtr += BufPtr-BufBeg;
	} else {
/*
 * it really must be a Q reg reference after all
 */
	    TCStEn = CStEnd;			/* save CStEnd */
	    CStEnd = EndArg;
	    Status = FindQR();
	    CStEnd = TCStEn;			/* restore CStEnd */
	    if (Status == FAILURE) {
		DBGFEX(2,DbgFNm,"FAILURE");
		return FAILURE;
	    }
	    if ((QR->End_P1-QR->Start) > (XBfEnd-BBfPtr)) {
		ErrMsg(ERR_STL);
		DBGFEX(2,DbgFNm,"FAILURE");
		return FAILURE;
	    }
	    MEMMOVE(BBfPtr, QR->Start, (SIZE_T)(QR->End_P1 - QR->Start));
	    BBfPtr += QR->End_P1 - QR->Start;
	}
    } else if ((*CBfPtr == 'U') || (*CBfPtr == 'u')) {
	if (++CBfPtr == EndArg) {
	    ErrMsg(ERR_ISS);
	    DBGFEX(2,DbgFNm,"FAILURE");
	    return FAILURE;
	}
	TCStEn = CStEnd;			/* save CStEnd */
	CStEnd = EndArg;
	Status = FindQR();
	CStEnd = TCStEn;			/* restore CStEnd */
	if (Status == FAILURE) {
	    DBGFEX(2,DbgFNm,"FAILURE");
	    return FAILURE;
	}
	*BBfPtr++ = (char)QR->Number;
    } else {
	*BBfPtr++ = CTRL_E;
	*BBfPtr++ = *CBfPtr;
    }
    DBGFEX(2,DbgFNm,"SUCCESS");
    return SUCCESS;
}


DEFAULT BldStr(XBfBeg, XBfEnd, XBfPtr)		/* build a string */
charptr XBfBeg;			/* beginning of build-string buffer */
charptr XBfEnd;			/* end of build-string buffer */
charptr (*XBfPtr);		/* pointer into build-string buffer */
{
    charptr	EndArg;		/* end of input string, plus 1 */
    unsigned char TmpChr;	/* temporary character */

    DBGFEN(2,"BldStr",NULL);

    if (FindES(ESCAPE) == FAILURE) {	/* move CBfPtr to end of argument */
	DBGFEX(2,DbgFNm,"FAILURE, FindES(ESCAPE) failed");
	return FAILURE;
    }

    WVFlag = '\0';		/* initialize ^W and ^V flag */
    CaseCv = IniSrM;		/* initialize internal search mode */
    BBfPtr = XBfBeg;		/* initialize ptr into build-string buffer */
    EndArg = CBfPtr;		/* save pointer to end of argument */
    CBfPtr = ArgPtr;		/* reset to beginning of argument */

    while (CBfPtr < EndArg) {
	if ((*CBfPtr == '^') && ((EdFlag & ED_CARET_OK) == 0)) {
	    if (++CBfPtr == EndArg) {
		ErrMsg(ERR_ISS);
		DBGFEX(2,DbgFNm,"FAILURE, no char after ^");
		return FAILURE;
	    }
	    TmpChr = To_Upper(*CBfPtr);
	    if ((TmpChr < '@') || (TmpChr > '_')) {
		ErrChr(ERR_IUC, *CBfPtr);
		DBGFEX(2,DbgFNm,"FAILURE, bad char after ^");
		return FAILURE;
	    }
	    TmpChr &= '\077';
	} else {
	    TmpChr = *CBfPtr;
	}

	switch (TmpChr) {

	    case CTRL_R:
	    case CTRL_Q:
		if (++CBfPtr == EndArg) {
		    ErrMsg(ERR_ISS);
		    DBGFEX(2,DbgFNm,"FAILURE");
		    return FAILURE;
		}
		*BBfPtr++ = *CBfPtr;
		break;

	    case CTRL_V:
	    case CTRL_W:
		if (DoCtVW(EndArg, TmpChr) == FAILURE) {
		    DBGFEX(2,DbgFNm,"FAILURE, DoCtVW failed");
		    return FAILURE;
		}
		break;

	    case CTRL_E:
		if (DoCtE(EndArg, XBfEnd) == FAILURE) {
		    DBGFEX(2,DbgFNm,"FAILURE, DoCtE failed");
		    return FAILURE;
		}
		break;

	    default:
		if (CaseCv == LOWER) {
		    TmpChr = To_Lower(TmpChr);
		} else if (CaseCv == UPPER) {
		    TmpChr = To_Upper(TmpChr);
		}
		*BBfPtr++ = TmpChr;
	}
	if (BBfPtr > XBfEnd) {
	    ErrMsg(ERR_STL);	/* string too long */
	    DBGFEX(2,DbgFNm,"FAILURE, string too long");
	    return FAILURE;
	}
	++CBfPtr;
    }
    *XBfPtr = BBfPtr;

#if DEBUGGING
    sprintf(DbgSBf,"string = \"%.*s\"", (int)(BBfPtr-XBfBeg), XBfBeg);
    DbgFEx(2,DbgFNm,DbgSBf);
#endif

    return SUCCESS;
}
