Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1798 lines
65 KiB

#include "cmd.h"
extern unsigned int DosErr;
extern jmp_buf CmdJBuf2 ; /* Used for error handling */
extern TCHAR DTNums[] ;
extern TCHAR MsgBuf[];
extern unsigned msglen; /* @@@@@@@@ */
extern TCHAR VerMsg[] ; /* dgs - print out cmd version too */
int Necho = 0; /* No echo option */
extern int KeysFlag; /* @@5 */
extern int EditLine(
CRTHANDLE DataPtr,
TCHAR *Buffer,
int MaxLength,
int *ReturnLength); /* @@5 */
static unsigned DataFlag ; /* Tells FillBuf where to get its input */
static CRTHANDLE DataPtr ; /* File handle/string ptr FillBuf... */
int Ctrlc = 0; /* flag - if set print a ctrl/c before next prompt */
int ExtCtrlc = 0; /* @@4 flag, if set print msg */
int AtIsToken; /* @@4 flag, true if @ is a token */
/***
* The lex buffer is called LexBuf. It holds characters as they are
* retrieved one by one by GetByte. With the advent of double byte
* characters, UnGetByte may be sometimes called upon to put back
* up to two characters. To facilitate this, LexBuf is really an
* alias for &LexBuffer[1]. This gives an extra byte in front of the
* buffer for character push back. Every time fillbuf is called, it
* copies the last character of the previous buffer into the byte
* preceeding the normal buffer. Thus, when UnGetByte does a
* LexBufPtr-- the pointer will correctly point at the preceeding character.
*/
TCHAR LexBuffer[LBUFLEN+3]; /* @@4 */
/* ...reads from Lexer input buffer *M011*/
/* LBUFLEN characters + newline + null + */
/* an extra byte for UnGetByte */
#define LexBuf (&LexBuffer[1])
static TCHAR *LexBufPtr ; /* Ptr to next byte in Lex's input buffer */
static TCHAR *PrevLexPtr ; /* M013 - New previous token pointer */
static TCHAR FrsBuf[LBUFLEN+1] ;
extern int LastRetCode ;
TCHAR LastRetCodeStr[32];
extern CHAR AnsiBuf[];
extern TCHAR Fmt27[], Fmt14[] ;
extern struct batdata *CurBat ; /* M026 - Batch file struct ptr */
extern TCHAR CrLf[] ;
extern TCHAR ErrStr[] ;
extern int NulNode ;
extern TCHAR Fmt19[] ;
extern TCHAR DBkSpc[] ;
#if defined(DBCS) // DDBkSpc[]
extern TCHAR DDBkSpc[] ;
#endif // defined(DBCS)
extern unsigned global_dfvalue; /* @@4 */
extern int EchoFlag ;
extern TCHAR PromptStr[], CurDrvDir[], Delimiters[] ;
extern unsigned flgwd ;
extern BOOL CtrlCSeen;
VOID SetCtrlC();
VOID ResetCtrlC();
//
// Prompt string special characters and associated print character/flag.
//
// These are the flags which may be placed in the flag field of the
// prompt_table structure to control PrintPrompt
//
#define PNULLFLAG 0
#define PTIMFLAG 1
#define PDATFLAG 2
#define PPATFLAG 3
#define PVERFLAG 4
#define PBAKFLAG 5 // destructive backspace flag
#define PNLNFLAG 6 // newline prompt flag
#define PDRVFLAG 7
#define PLITFLAG 8 // Print character in SpecialChar field
#define PDPTFLAG 9 // Print depth of pushd stack
#define PNETFLAG 10 // Print \\server\share or local for current drive
//
// Esc character used to mark a special prompt char. in prompt string
//
#define PROMPTESC DOLLAR
//
// Table of prompts for user.
// BUGBUG This should be in a message file!!!!!!
//
typedef struct {
TCHAR Char; // Used to match esc. char. in user prompt
TCHAR Format; // Used to print some string that has to be computed
TCHAR Literal; // When Format == PLITFLAG this is printed in prompt
} PROMPT_ENTRY;
PROMPT_ENTRY PromptTable[] = {
{ TEXT('P'),PPATFLAG, NULLC },
{ TEXT('E'),PLITFLAG,'\033' },
{ TEXT('D'),PDATFLAG, NULLC },
{ TEXT('T'),PTIMFLAG, NULLC },
{ TEXT('B'),PLITFLAG, PIPOP },
{ TEXT('G'),PLITFLAG, OUTOP },
{ TEXT('H'),PBAKFLAG, NULLC },
{ TEXT('L'),PLITFLAG, INOP },
{ TEXT('N'),PDRVFLAG, NULLC },
{ TEXT('S'),PLITFLAG, SPACE },
{ TEXT('Q'),PLITFLAG, EQ },
{ TEXT('V'),PVERFLAG, NULLC },
{ TEXT('_'),PNLNFLAG, NULLC },
{ DOLLAR, PLITFLAG, DOLLAR },
{ TEXT('A'),PLITFLAG, ANDOP },
{ TEXT('C'),PLITFLAG, LPOP },
{ TEXT('F'),PLITFLAG, RPOP },
{ TEXT('+'),PDPTFLAG, NULLC },
{ TEXT('M'),PNETFLAG, NULLC },
{ NULLC,PNULLFLAG, NULLC}};
/*** InitLex - initialize the lexer's global variables
*
* Purpose:
* Initialize DataFlag, DataPtr, LexBuf, and LexBufPtr.
*
* InitLex(unsigned dfvalue, int dpvalue)
*
* Args:
* dfvalue - the value to be assigned to DataFlag
* dpvalue - the value to be assigned to DataPtr
*
*/
void InitLex(dfvalue, dpvalue)
unsigned dfvalue ;
int dpvalue ;
{
DataFlag = dfvalue ;
DataPtr = dpvalue ;
*LexBuf = NULLC ;
PrevLexPtr = LexBufPtr = LexBuf ; /* M013 - Init new ptr */
DEBUG((PAGRP, LXLVL, "INITLEX: Dataflag = %04x DataPtr = %04x", DataFlag, DataPtr)) ;
}
/*** Lex - controls data input and token lexing
*
* Purpose:
* Read in the next token or argstring and put it in tokbuf.
*
* unsigned Lex(TCHAR *tokbuf, unsigned lflag)
*
* Args:
* tokbuf - buffer used by lex to store the next token or
* - M013 if zero, indicates unget last token.
* lflag - bit 0 on if lex is to return an argument string, ie white space
* other than NLN is not considered a token delimiter
*
* Returns:
* If the token is an operator, EOS, or NLN ret the 1st byte of the token.
* If the token is a command, REM arg or argstring, return TEXTOKEN.
* If the token is longer than MAXTOKLEN or the token is illegal, LEXERROR
* is returned.
*
* Notes:
* The parser depends on the fact that the only values returned by
* Lex that are greater than 0xff are TEXTOKEN and LEXERROR.
*
*/
unsigned Lex(tokbuf, lflag)
TCHAR *tokbuf ;
unsigned lflag ;
{
int i ; /* Length of text token */
TCHAR c, /* Current character */
*tbcpy; /* Copy of tokbuf */
if(setjmp(CmdJBuf2)) { /* M026 - Now msg printed prior */
return((unsigned)LEXERROR) ; /* ...to arrival here */
} ;
/* M013 - This code detects request to unget last token and if so, performs
that function. If not, it sets the previous token pointer to
to equal the current token pointer.
*/
if (tokbuf == LX_UNGET) { /* Unget last token? */
DEBUG((PAGRP, LXLVL, "LEX: Ungetting last token.")) ;
LexBufPtr = PrevLexPtr ; /* If so, reset ptr... */
return(LX_UNGET) ; /* ...and return */
} else { /* If not, set previous... */
PrevLexPtr = LexBufPtr ; /* ...ptr to current... */
DEBUG((PAGRP, LXLVL, "LEX: lflag = %d", lflag)) ;
} ; /* ...ptr and continue */
/* M013 ends */
tbcpy = tokbuf ;
/* M005 - Altered conditional below to also fail if the LX_REM bit
* is set making it "If !(arg | rem), eat whtspc & delims".
*/
if (!(lflag & (LX_ARG|LX_REM))) {
DEBUG((PAGRP, LXLVL, "LEX: Trashing white space.")) ;
while (TRUE) {
c = GetByte();
if (((_istspace(c) && c != NLN)
|| (mystrchr(((lflag & LX_EQOK) ? &Delimiters[1] : Delimiters), c) && c)))
;
else
break;
} ;
UnGetByte() ;
} ;
/* As of M016, operators of more than 2 characters can be lexed. For now,
* these are assumed to be specific-handle redirection operators of the form
* 'n>>' or 'n<<' and always begin with a digit. TextCheck will not return
* a digit as an operator unless it is preceeded by whitespace and followed
* by '>' or '<'. To simplify matters, handle substitution (ie., '...&n')
* is now lexed as part of a special five character operator, instead of
* looking at the '&n' as an argument. ASCII filename arguments, however,
* are still lexed as separate tokens via another call to Lex.
*/
if (TextCheck(&c, &lflag) == LX_DELOP) {
*tokbuf++ = c ; /* The token is an operator */
if (_istdigit(c)) { /* Next is '<' or '>'... */
DEBUG((PAGRP, LXLVL, "LEX: Found digit operator.")) ;
c = GetByte() ; /* ...by definition or ... */
*tokbuf++ = c ; /* ...we wouldn't be here */
} ;
if (c == PIPOP || c == ANDOP || c == OUTOP || c == INOP) {
if ((c = GetByte()) == *(tokbuf-1)) {
*tokbuf++ = c ;
c = GetByte() ;
} ;
if (*(tokbuf-1) == OUTOP || *(tokbuf-1) == INOP) {
DEBUG((PAGRP,LXLVL, "LEX: Found redir.")) ;
if (c == CSOP) {
DEBUG((PAGRP,LXLVL, "LEX: Found >&")) ;
*tokbuf++ = c ;
do {
c = GetByte() ;
} while (_istspace(c) ||
mystrchr(Delimiters,c)) ;
if (_istdigit(c)) {
*tokbuf++ = c ;
c = GetByte() ;
} ;
} ;
/* M016 ends */
} ;
UnGetByte() ;
} ;
*tokbuf = NULLC ;
DEBUG((PAGRP, LXLVL, "LEX: Returning op = `%ws'", tbcpy)) ;
return(*tbcpy) ;
} ;
DEBUG((PAGRP, LXLVL, "LEX: Found text token %04x, Getting more.", c)) ;
*tokbuf++ = c ; /* Found text token, read the rest */
lflag |= LX_DBLOK;
AtIsToken = 0; /* @@4, treat @ as text now */
for (i = 1 ; TextCheck(&c, &lflag) != LX_DELOP && i < MAXTOKLEN ; i++)
*tokbuf++ = c ;
lflag &= ~LX_DBLOK;
*tokbuf = NULLC ;
if (i < MAXTOKLEN)
UnGetByte() ;
if (i >= MAXTOKLEN && c != (TCHAR) -1) { /* Token too long, complain */
/* M025 */ PutStdErr(MSG_TOKEN_TOO_LONG, ONEARG, argstr1(TEXT("%s"), (unsigned long)((int)tokbuf)));
return((unsigned)LEXERROR) ;
} ;
DEBUG((PAGRP, LXLVL, "LEX: Return text = `%ws' type = %04x", tbcpy, TEXTOKEN)) ;
return(TEXTOKEN) ;
}
/*** TextCheck - get the next character and determine its type
*
* Purpose:
* Store the next character in LexBuf in *c. If that character is a
* valid text token character, return it. Otherwise return LX_DELOP.
*
* int TextCheck(TCHAR *c, unsigned &lflag)
*
* Args:
* c - the next character in the lexer input buffer is stored here.
* lflag - Bit 0 = On if lex is to return an argument string, ie.,
* white space other than NLN is not a token delimiter.
* Bit 1 = On if a quoted string is being read, ie., only NLN
* or a closing quote are delimiters.
* Bit 2 = On if equalsigns are NOT to be considered delimiters.
* Bit 3 = On if left parens are to be considered operators.
* Bit 4 = On if right parens are to be considered operators.
* Bit 5 = On if only NLN is to be a delimiter.
* Bit 6 = On iff the caller is willing to accept the second
* half of a double byte character
*
* Returns:
* Next character or LX_DELOP if a delimeter/operator character is found.
*
*/
int TextCheck(c, lflag)
TCHAR *c ;
unsigned *lflag ;
{
TCHAR i ; /* M016 - Temp byte holder */
static int saw_dbcs_lead = 0; /* remember if we're in the middle
of a double byte character */
*c = GetByte() ;
if (saw_dbcs_lead) {
saw_dbcs_lead = 0;
if (*lflag & LX_DBLOK) /* If second half of double byte is */
return(*c); /* ok, return it, otherwise. . . */
else
*c = GetByte(); /* go on to next character */
}
DEBUG((PAGRP, BYLVL, "TXTCHK: c = %04x lflag = %04x", *c, *lflag)) ;
switch (*c) {
case SILOP: /* M017 - New unary operator */
/* ...binds like left paren */
if ((*lflag & (LX_QUOTE|LX_REM))) /* M005 */
break ;
if( !AtIsToken ) /* If @ is not to be treated */
{ /* as token, then indicate */
return( *c ); /* such @@4 */
} ;
case LPOP: /* M002 - Moved these two cases */
if ((*lflag & (LX_QUOTE|LX_REM))) /* M005 */
break ;
if(!(*lflag & GT_LPOP)) /* ...up and break if */
break ; /* ...they are not to */
case RPOP: /* ...be treated as ops */
if ((*lflag & (LX_QUOTE|LX_REM))) /* M005 */
break ;
if((!(*lflag & GT_RPOP)) && *c == RPOP)
break ; /* M002 ends */
case NLN: /* M005 - NLN turns off QUOTE/REM flags */
case EOS: /* @@5a - treat like NLN */
*lflag &= (~LX_QUOTE & ~LX_REM) ; /* M005 */
case CSOP:
case INOP: /* M005 - Note below that LX_DELOP... */
case PIPOP: /* ...QUOTE mode or REM mode is in... */
case OUTOP: /* ...in effect at the time */
if (!(*lflag & (LX_QUOTE|LX_REM))) /* M005 */
return(LX_DELOP) ;
} ;
/* M003 - If the character is '^', and the QUOTE mode flag is off,
* discard the current character, get the next one and return
* it as text.
* M005 - Extended this conditional to insure that both QUOTE and
* REM flags must be off for "escape" to occur.
*/
if (*c == ESCHAR && !(*lflag & (LX_QUOTE|LX_REM))) {
*c = GetByte() ;
if (*c == NLN)
*c = GetByte() ;
return(*c) ;
} ;
/* M003/M005 end */
if (*c == QUOTE) /* Flip quote mode flag bit */
*lflag ^= LX_QUOTE ;
/* M005 - Altered conditional below to also insure that REM flag was
* off before checking for any delimiters
*/
if (!(*lflag & (LX_ARG|LX_QUOTE|LX_REM)) &&
(_istspace(*c) ||
mystrchr(((*lflag & LX_EQOK) ? &Delimiters[1] : Delimiters), *c)))
return(LX_DELOP) ;
/* As of M016, before accepting this character as text, it is now tested
* for being a digit followed by one of the redirection operators and;
* 1) is the first character on a line, 2) is preceeded by whitespace or
* 3) is preceeded by a delimiter (including Command's operators). If it
* meets these conditions, it is a special, specific-handle redirection
* operator and TextCheck must return LX_DELOP so that Lex can build the
* remainder. NOTE: LexBufPtr is advanced during GetByte, so that the
* current byte is *(LexBufPtr-1).
*/
if (_istdigit(*c)) {
DEBUG((PAGRP,BYLVL,"TXTCHK: Found digit character.")) ;
if ((LexBufPtr-LexBuf) < 2 ||
_istspace(i = *(LexBufPtr-2)) ||
mystrchr(TEXT("()|&=,;\""), i)) {
DEBUG((PAGRP,BYLVL,"TXTCHK: Digit follows delim.")) ;
if (*LexBufPtr == INOP || *LexBufPtr == OUTOP) {
DEBUG((PAGRP,BYLVL,"TXTCHK: Found hdl redir")) ;
if (!(*lflag & (LX_QUOTE|LX_REM))) /* M005 */
return(LX_DELOP) ;
} ;
} ;
} ;
/* M016 ends */
return(*c) ;
}
/*** GetByte - return the next byte in the buffer
*
* Purpose:
* Get the next byte from the lexer's input buffer. If the buffer is
* empty, fill it first. Update the buffer pointer.
*
* TCHAR GetByte()
*
* Return:
* The next character in the buffer or EOF.
*
* Notes:
* All three types of input STDIN, FILE and STRING are treated
* the same now when it comes to dealing with CR/LF combinations.
* Keyboard input is massaged to look like file input.
* Invalid double byte characters are thrown away and not returned
* to the caller.
*
*/
TCHAR GetByte()
{
static int saw_dbcs_lead = 0; /* remember if we're in the middle
of a double byte character */
TCHAR lead; /* variables for remember parts of
double byte characters */
if (!*LexBufPtr)
FillBuf() ;
DEBUG((PAGRP, BYLVL, "GTTCHAR: byte = %04x", *LexBufPtr)) ;
if (*LexBufPtr == CR && !saw_dbcs_lead) {
/* M000 - removed file-only test */
LexBufPtr++ ;
return(GetByte()) ;
} ;
/* if this is a double byte character, look ahead to the next character
and check for legality */
if (saw_dbcs_lead) {
saw_dbcs_lead = 0;
return(*LexBufPtr++) ;
}
else {
lead = *LexBufPtr++;
return(lead);
}
}
/*** UnGetByte - rewind the lexer buffer pointer 1 byte
*
* Purpose:
* Back up the lexer's buffer pointer. If the pointer already points
* to the beginning of the buffer, do nothing.
*
* UnGetByte()
*
* Return:
* Nothing.
*
*/
void UnGetByte()
{
if (!(LexBufPtr == LexBuffer))
LexBufPtr-- ;
}
/*** FillBuf - read data to fill the lexer's buffer
*
* Purpose:
* To fill the lexer's buffer with data from whatever source is indicated
* by the global variables DataFlag and DataPtr. If reading from
* stdin, prompt for data.
*
* FillBuf()
*
* Notes:
* - Algorithm after M021 is as follows:
* copy last char of current buffer into LexBuffer[0] (which preceeds
* LexBuf by one byte) so the UnGetByte can unget two bytes
* If READSTDIN or READFILE
* If input is STDIN
* Print correct prompt
* Use DOSREAD to fill primary buffer
* Copy to Lexer buffer so that primary buffer is usable by F3 key.
* Null terminate total input.
* Scan buffer for NLN || ^Z
* If none found
* Error out
* Else
* Terminate statement at NLN or ^Z (exclude ^Z iteself)
* If read was from file
* Rewind to end of first statement
* If file handle is STDIN
* Echo statement to STDOUT
* Else
* Read first statement from string and reset pointer
* Reset Lexer Buffer Pointer to start of buffer
* Substitute for batch and environment variables (M026)
* Reset Previous Lexer Buffer Pointer to start of buffer
*
*/
BOOL ReadFromStdInOkay = FALSE;
void FillBuf()
{
long l ; /* M004 - Data count in buffer */
TCHAR *sptr ; /* Copy of DataPtr */
size_t i ; /* Work variable */
DWORD cnt ; /* Count of input bytes */
BOOL flag;
//
// clear this flag in case it was hit from previous command
// if it is true we would not execute the next command
//
ResetCtrlC();
LexBuffer[0] = *(LexBufPtr - 1);
switch (DataFlag & FIRSTIME) {
case READFILE:
case READSTDIN:
if ((DataFlag & FIRSTIME) == READSTDIN ||
DataPtr == STDIN) {
if (DataFlag & NOTFIRSTIME) {
/* M025 */ PutStdOut(MSG_MS_MORE, NOARGS);
} else {
PrintPrompt() ;
DataFlag |= NOTFIRSTIME ;
DEBUG((PAGRP, LFLVL, "FLBF: Reading stdin")) ;
} ;
} ;
//
// clear in case ^c seen while printing prompt
//
ResetCtrlC();
DEBUG((PAGRP, LFLVL, "FLBF: Reading handle %d", DataPtr)) ;
//
// If input is STDIN and piped or input is from a
// device but not console input (flgwd == 1)
//
if ( ( DataPtr == STDIN ) && ( FileIsPipe( STDIN ) ||
( FileIsDevice( STDIN ) && (!(flgwd & 1)) ) ) ) {
cnt = 0;
while (
( cnt < LBUFLEN) && /* ##1 */
( (ReadBufFromFile(CRTTONT(DataPtr),
&FrsBuf[cnt], 1, (LPDWORD)&i)) != 0 ||
GetLastError() == ERROR_MORE_DATA) &&
( i != 0 )
) {
cnt++;
if ( FrsBuf[cnt-1] == NLN ){
break;
} /* endif */
}
} else if ( ( DataPtr == STDIN ) &&
FileIsDevice( STDIN ) &&
(flgwd & 1) ) {
//
// Are reading from stdin and it is a device
// (not a file) and it is console input
//
if ( KeysFlag ) {
i = EditLine( DataPtr, FrsBuf, LBUFLEN, &cnt );
}
else {
ResetCtrlC();
if (ReadBufFromConsole(
CRTTONT(DataPtr),
FrsBuf,
LBUFLEN,
&cnt) ) {
//
// Check that ^c's on the current line.
// Could be the case where ^c thread
// came in from a previous line
//
//
// also if cnt is 0 then outpt crlf to
// prevent two prompts on command line
//
if (cnt == 0) {
if (GetLastError() == ERROR_OPERATION_ABORTED) {
cmd_printf(CrLf);
longjmp(CmdJBuf2, -1);
}
cmd_printf(CrLf);
}
i = 0;
DEBUG((PAGRP, LFLVL, "FLBF: ReadFile %d bytes", cnt)) ;
} else {
cnt = 0;
i = GetLastError();
DEBUG((PAGRP, LFLVL, "FLBF: ReadFile %d bytes and error %d", cnt, i)) ;
}
}
}
else {
flag = ReadBufFromFile(
CRTTONT(DataPtr),
FrsBuf, LBUFLEN, (LPDWORD)&cnt) ;
DEBUG((PAGRP, LFLVL, "FLBF: Read %d bytes", cnt)) ;
if (CtrlCSeen) {
ResetCtrlC();
longjmp(CmdJBuf2, -1);
// Abort();
}
if (flag == 0 || (int)cnt <= 0) {
cnt = 0;
i = GetLastError();
}
else {
i = 0;
}
}
DEBUG((PAGRP, LFLVL, "FLBF: I made it here alive")) ;
if (!cnt && DataPtr == STDIN) {
DEBUG((PAGRP,LFLVL,"FLBF: ^Z from STDIN!")) ;
DEBUG((PAGRP,LFLVL," READFILE retd %d",i)) ;
if (FileIsDevice(STDIN) && ReadFromStdInOkay) {
DEBUG((PAGRP,LFLVL,"FLBF: Is device, fixing up buffer")) ;
FrsBuf[0] = NLN ;
++cnt ;
} else {
DEBUG((PAGRP,LFLVL,"FLBF: Is file, aborting!!!")) ;
ExitAbort(EXIT_EOF) ;
} ;
} else if (!ReadFromStdInOkay && cnt && DataPtr == STDIN) {
ReadFromStdInOkay = TRUE;
}
cnt = LexCopy(LexBuf, FrsBuf, cnt);
DEBUG((PAGRP, LFLVL, "FLBF: Received %d characters.", cnt)) ;
*(LexBuf+cnt) = NULLC ; /* Term with NULL */
/* Have total bytes read. Now scan for NLN or ^Z. Either means end of
* input statement, neither in 128 bytes means buffer overflow error.
*/
if((i = mystrcspn(LexBuf, TEXT("\n\032"))) < mystrlen(LexBuf)
|| cnt == 0) { /*M029*/
DEBUG((PAGRP, LFLVL, "FLBF: Scan found %04x", *(LexBuf+i))) ;
DEBUG((PAGRP, LFLVL, "FLBF: At position %d", i)) ;
sptr = LexBuf+i ; /* Set pointer */
if(*sptr == CTRLZ) {
*sptr = NLN;
}
if(*sptr == NLN) { /* If \n, inc... */
++sptr ; /* ...ptr & sub */
l = cnt - ++i ; /* ....whats used */
/* M014 ends */ i = FILE_CURRENT ;
} else { /* If ^Z, go EOF */
l = 0 ;
i = FILE_END ;
} ;
DEBUG((PAGRP,LFLVL,"FLBF: Changing %04x to NULLC",*(sptr-1))) ;
*sptr = NULLC ; /* Term valid input */
if (!FileIsDevice(DataPtr)) {
SetFilePointer(CRTTONT(DataPtr), -l, NULL, i) ;
DEBUG((PAGRP, LFLVL, "FLBF: Rewound %ld", l)) ;
if ((DataPtr == STDIN) && (!Necho)) {
cmd_printf(Fmt14, LexBuf) ;
} ;
} ;
} else if(i >= LBUFLEN) { /*M029*/
/* @@4 */ if ( global_dfvalue == READFILE )
/* @@4 */ {
/* @@4 */ if ( EchoFlag == E_ON )
/* @@4 */ {
/* @@4 */ DEBUG((PAGRP, LXLVL,
/* @@4 */ "BLOOP: Displaying Statement.")) ;
/* @@4 */
/* @@4 */ PrintPrompt() ;
/* @@4 */ cmd_printf(&LexBuffer[1]) ;
/* @@4 */ cmd_printf(CrLf) ;
/* @@4 */ } ;
/* @@4 */ PutStdErr(MSG_LINES_TOO_LONG, NOARGS) ;
/* @@4 */ Abort() ;
/* @@4 */ } ;
PutStdErr(MSG_LINES_TOO_LONG, NOARGS) ;
/* M028 */ if(DataPtr == STDIN && FileIsDevice(DataPtr))
FlushKB() ;
longjmp(CmdJBuf2,-1) ;
} ;
break ;
case READSTRING:
DEBUG((PAGRP, LFLVL, "FLBF: Reading string.")) ;
*(LexBuf+LBUFLEN) = NULLC ; /* Term max string */
_tcsncpy(LexBuf, (TCHAR *) DataPtr, LBUFLEN) ;
DataPtr += mystrlen(LexBuf)*sizeof(TCHAR) ; /* Update DataPtr */
DEBUG((PAGRP, LFLVL, "FLBF: New DataPtr = %ws", DataPtr)) ;
break ;
} ;
LexBufPtr = LexBuf ; /* M004 - Reset pointer */
SubVar() ; /* Sub env & batch vars (M026) */
DEBUG((PAGRP, LFLVL, "FLBF: Buffer contents: `%ws'", LexBufPtr)) ;
/* Insure that when buffer is refilled, the previous token pointer is
* reset to the start of the lexer buffer
*/
PrevLexPtr = LexBufPtr ;
}
/*** LexCopy - copy the lex buffer
*
* Purpose:
* To copy the contents read into the dos buffer into LexBuf,
* translating double byte spaces into regular spaces in the
* process.
* Input:
* A to and from pointer and a byte count.
* Returned:
* A new byte count which might be smaller than that passed in.
*/
int LexCopy(to, from, count)
TCHAR *to, *from;
int count;
{
int new_count;
_tcsncpy( to, from, count );
return count;
}
BOOLEAN PromptValid;
TCHAR PromptVariableBuffer[ 256 ];
TCHAR PromptBuffer[ 256 ];
void
PrintPrompt()
/*++
Routine Description:
To print Command's main input prompt and to interpret the special
characters in it (see MSDOS manual for list of special prompt
characters).
An array of PROMPT_ENTRY structures called PromptTable is searched for
the special characters. If a match is found , then either print out
the special character if the format field is PLITFLAG or do some
sort of special processing to print out the prompt string such as
get time of day etc.
Arguments:
Return Value:
--*/
{
TCHAR *pszPrompt ;
TCHAR *s;
int nLeft, nUsed;
ULONG idx;
ULONG vrs ;
#if defined(JAPAN) && defined(UNICODE)
// This local variable is used for determine the last
// character is full width character (=DBCS) or not.
WCHAR chLast = NULLC;
#endif /* defined(JAPAN) && defined(UNICODE) */
#if 0
//
// used in pipe if a ^c see on dispatch of left side
//
if (ExtCtrlc) {
if (ExtCtrlc == 1)
PutStdOut(MSG_CMD_KILLED, NOARGS);
ExtCtrlc = 0;
}
if (Ctrlc) {
PutStdOut(MSG_C, NOARGS);
Ctrlc = 0;
}
#endif
if (CtrlCSeen) {
PutStdOut(MSG_C, NOARGS);
ResetCtrlC();
// Abort();
}
//
// The newline which must preceed the prompt is tied to prompt rather than to
// command completion in Dispatch.
//
// Also return without newline or prompt if echo status is "echo off".
//
if (EchoFlag == E_OFF) {
return ;
}
if (!NulNode) {
cmd_printf(CrLf) ;
}
if ( PromptValid ) {
pszPrompt = PromptVariableBuffer;
}
else {
//
// Fetch user prompt string from environment (should be PROMPT)
//
pszPrompt = GetEnvVar(PromptStr) ;
if ( pszPrompt ) {
mystrcpy( PromptVariableBuffer, pszPrompt);
pszPrompt = PromptVariableBuffer;
PromptValid = TRUE;
}
}
//
// refetch the current directory, since we may have lost the
// drive letter due to net disconnect
//
GetDir(CurDrvDir, GD_DEFAULT) ;
DEBUG((PAGRP, LFLVL, "PRINTPROMPT: pszPrompt = `%ws'", pszPrompt)) ;
s = PromptBuffer;
*s = NULLC;
nLeft = sizeof(PromptBuffer) / sizeof(TCHAR);
//
// Check if there was a prompt string.
// If there is not prompt string then just print current drive
//
if (!pszPrompt || !*pszPrompt) {
nUsed = _sntprintf( s, nLeft, Fmt27, CurDrvDir) ;
s += nUsed;
nLeft -= nUsed;
} else {
//
// Loop through interpreting prompt string
//
for ( ; *pszPrompt ; pszPrompt++) {
//
// Look for the escape character in prompt for special
// processing
//
if (*pszPrompt != PROMPTESC) {
nUsed = _sntprintf( s, nLeft, Fmt19, *pszPrompt) ;
s += nUsed;
nLeft -= nUsed;
#if defined(JAPAN) && defined(UNICODE)
// If character is full width character, mark it.
if (IsFullWidth(*pszPrompt))
chLast = pszPrompt;
else
chLast = NULLC;
#endif /* defined(JAPAN) && defined(UNICODE) */
} else {
//
// There is an escape character in prompt string.
// Try to find a match for next character after escape
// character from prompt table.
//
pszPrompt++;
for (idx = 0 ; PromptTable[idx].Char != NULLC ; idx++)
if (_totupper(*pszPrompt) == PromptTable[idx].Char) {
break ;
}
if (PromptTable[idx].Char == NULLC) {
//
// Could find no match for escape. Return without finishing
// printing
//
//
// If ^c seen while printing prompt blow it away
//
if (CtrlCSeen) {
ResetCtrlC();
}
return ;
} else {
if (PromptTable[idx].Format == PLITFLAG) {
nUsed = _sntprintf( s, nLeft, Fmt19, PromptTable[idx].Literal) ;
s += nUsed;
nLeft -= nUsed;
} else {
switch (PromptTable[idx].Format) {
case PTIMFLAG:
nUsed = PrintTime(NULL, PT_TIME, s, nLeft) ;
s += nUsed;
nLeft -= nUsed;
break ;
case PDATFLAG:
nUsed = PrintDate(NULL, PD_DATE, s, nLeft) ;
s += nUsed;
nLeft -= nUsed;
break ;
case PPATFLAG:
nUsed = _sntprintf( s, nLeft, Fmt14, CurDrvDir) ;
s += nUsed;
nLeft -= nUsed;
break ;
case PVERFLAG:
vrs = GetVersion();
GetMsg( MSG_MS_DOS_VERSION,
THREEARGS,
VerMsg,
argstr1( TEXT("%d"), (unsigned long)(vrs & 0xFF)),
argstr2( TEXT("%-2d"), (unsigned long)((vrs >> 8)& 0xFF))
);
nUsed = _sntprintf( s, nLeft, Fmt14, MsgBuf );
s += nUsed;
nLeft -= nUsed;
break ;
case PBAKFLAG:
#if defined(DBCS) // PrintPrompt()
// if the last character is full width character.
// we should delete 2 bytes.
if (chLast != NULLC)
nUsed = _sntprintf( s, nLeft, DDBkSpc);
else
nUsed = _sntprintf( s, nLeft, DBkSpc) ;
#else
nUsed = _sntprintf( s, nLeft, DBkSpc) ;
#endif // defined(DBCS)
s += nUsed;
nLeft -= nUsed;
break ;
case PNLNFLAG:
nUsed = _sntprintf( s, nLeft, CrLf) ;
s += nUsed;
nLeft -= nUsed;
break ;
case PDPTFLAG:
case PNETFLAG:
//
// If extensions are enabled, then two new prompt characters
//
// $+ generates from zero to N plus characters, depending upon
// the depth of the PUSHD directory stack.
//
// $m generates the empty string if the current drive is not a
// network drive. If it is, then $m generates the \\server\share
// name with a trailing space.
//
if (fEnableExtensions) {
if (PromptTable[idx].Format == PDPTFLAG) {
nUsed = _sntprintf( s, nLeft, TEXT("%.*s"), GetDirStackDepth(), TEXT("+++++++++++++++++++++++++++++++++"));
s += nUsed;
nLeft -= nUsed;
}
else {
TCHAR CurDrive[4];
TCHAR NetPath[MAX_PATH];
DWORD n;
_tcsncpy(CurDrive, CurDrvDir, 2);
CurDrive[2] = NULLC;
n = MAX_PATH;
switch (WNetGetConnection(CurDrive,
NetPath,
&n
)
) {
case NO_ERROR:
NetPath[n] = NULLC;
nUsed = _sntprintf( s, nLeft, TEXT("%s "), NetPath);
s += nUsed;
nLeft -= nUsed;
break;
case ERROR_NOT_CONNECTED:
break;
default:
nUsed = _sntprintf( s, nLeft, TEXT("Unknown"));
s += nUsed;
nLeft -= nUsed;
break;
}
}
}
break;
default:
nUsed = _sntprintf( s, nLeft, Fmt19, CurDrvDir[0]) ;
s += nUsed;
nLeft -= nUsed;
}
}
}
}
} // for
} // else
*s = NULLC;
cmd_printf(TEXT("%s"), PromptBuffer);
//
// If ^c seen while printing prompt blow it away
//
if (CtrlCSeen) {
ResetCtrlC();
}
}
/*** IsData - check the input buffer
*
* Purpose:
* Check the lexer's input buffer to see if there is data in it.
*
* int IsData()
*
* Returns:
* TRUE if the buffer has data in it.
* FALSE if the buffer is empty.
*
*/
int IsData()
{
DEBUG((PAGRP, LXLVL, "ISDATA: *LexBufPtr = %04x", *LexBufPtr)) ;
if (*LexBufPtr)
return(TRUE) ;
return(FALSE) ;
}
/*** SubVar - Substitute for environment variables. (M004)
*
* Purpose:
* This function scans the lexer input buffer looking for percent
* signs and substituting batch variables and environment variables
* as they are found.
*
* void SubVar()
*
* NOTES:
* - This function does not return if expansion causes length to exceed
* maximum line length (LBUFLEN = 2048).
* - M026 caused complete rewrite to perform batch variable substitution
* at the lexer stage rather than in batch processing. Note that the
* printing of error messages can now be either line too long or token
* too long, so error printing occurs before the longjmp() call.
*/
void SubVar()
{
TCHAR lxtmp[LBUFLEN+1] ; /* Temporary holding buffer */
int dlen ; /* Temps & counters */
int j, slen ;
TCHAR *srcp = lxtmp ; /* Src byte pointer */
TCHAR *substr = NULL ; /* Possible Env Var pointer */
TCHAR c ; /* Temp byte holder */
mystrcpy(srcp,LexBufPtr) ; /* Make a copy of the input */
DEBUG((PAGRP, LXLVL, "SBENV: Copy = %ws", srcp)) ;
dlen = j = slen = 0 ; /* Zero the counters */
while((c = *srcp++) && dlen <= LBUFLEN + 1) {
if(c != PERCENT) {
*LexBufPtr++ = c ;
++dlen ;
if(c == NLN) /* Stop subst. if statement end */
break ;
continue ;
} ;
DEBUG((PAGRP,LXLVL,"SBVAR: Found `%%' in input")) ;
DEBUG((PAGRP,LXLVL,"SBVAR: Current pair is `%c%c'",c,*srcp)) ;
if (CurBat && *srcp == PERCENT) {
DEBUG((PAGRP,LXLVL,"SBVAR: Found `%%%%' in batch file")) ;
*LexBufPtr++ = *srcp++ ;
++dlen ;
continue ;
} ;
//
// If inside a command script and extensions are enabled,
// expand %* into all the arguments (%1 through %n).
//
if (CurBat && fEnableExtensions && *srcp == STAR) {
++srcp ; /* Kick past star */
slen = mystrlen(CurBat->orgargs);
substr = CurBat->orgargs;
DEBUG((PAGRP,LXLVL,"SBVAR: Found batch var %*")) ;
DEBUG((PAGRP,LXLVL,"SBVAR: - len = %d", slen)) ;
DEBUG((PAGRP,LXLVL,"SBVAR: - var = %ws", substr)) ;
if (slen > 0) {
if (dlen+slen > MAXTOKLEN) {
DEBUG((PAGRP,LXLVL,"SBVAR: Too Long!"));
_tcsncpy(LexBufPtr,substr,MAXTOKLEN - dlen) ;
LexBuf[MAXTOKLEN] = NULLC ;
PutStdErr(MSG_TOKEN_TOO_LONG, ONEARG,LexBuf) ;
longjmp(CmdJBuf2,-1) ;
} ;
mystrcpy(LexBufPtr, substr) ;
dlen += slen ;
LexBufPtr += slen ;
DEBUG((PAGRP,LXLVL,"SBVAR: Subst complete; dest = `%ws'", LexBuf)) ;
} else {
DEBUG((PAGRP,LXLVL,"SBVAR: Var %* undefined")) ;
} ;
continue ;
} ;
//
// If inside a command script attempt to expand variable references
// of the form %n where n is a digit from 0 to 9
//
// If not in a command script or not a variable reference see if
// this is an environment variable expansion request.
//
if((CurBat &&
(substr = MSCmdVar(&CmdJBuf2,srcp,&j,TEXT("0123456789"),CurBat->aptrs))
) ||
(substr = MSEnvVar(&CmdJBuf2,srcp,&j, PERCENT)) != NULL
) {
DEBUG((PAGRP,LXLVL,"SBVAR: Found var %ws", substr)) ;
//
// Either variable reference or environment variable reference.
// Copy the result to the input buffer
//
if((dlen += (slen = mystrlen(substr))) > LBUFLEN+1) {
PutStdErr(MSG_LINES_TOO_LONG, NOARGS);
longjmp(CmdJBuf2,-1) ;
} ;
mystrcpy(LexBufPtr,substr) ;
LexBufPtr += slen ;
srcp += j ; /* M027 'j+1' --> 'j' */
} else {
DEBUG((PAGRP,LXLVL,"SBVAR: No var found")) ;
//
// Variable not found. If inside of command script, toss
// the variable reference in the bit bucket. If not in a
// command script pass the characters that make up the reference
// into the input buffer. User will see their mistake shortly.
//
if (CurBat) {
srcp += j ;
} else {
*LexBufPtr++ = c ;
dlen++ ;
} ;
} ;
} ;
*LexBufPtr = NULLC ; /* Terminate Statement */
LexBufPtr = LexBuf ; /* Reset Pointer to start */
if(dlen > LBUFLEN+1) { /* Statement too long?? */
*LexBufPtr = NULLC ; /* If so, kill line, print err */
PutStdErr(MSG_LINES_TOO_LONG, NOARGS);
longjmp(CmdJBuf2,-1) ;
} ;
}
/*** MSEnvVar - Does environment variable substitution
*
* Purpose:
* When percent signs are found in the newly filled lexer buffer,
* this function is called to determine if there is an environment
* variable substitution possible.
*
* TCHAR *MSEnvVar(TCHAR *str, int *supdate, TCHAR delim)
*
* Args:
* errjmp - optional pointer to jmp_buf for errors
* str - pointer to a possible environment variable name
* supdate - location to place env variable name length in
* delim - delimiter character to look for (e.g. PERCENT)
*
* Returns:
* If there is no ending delim,
* set supdate to 0
* return NULL
* else
* set supdate to the enclosed string length
* if the string is not an environment variable
* return NULL
* else
* return a pointer to the replacement string
*
* Notes:
* - M026 changed the way this function works so that supdate will
* contain the string length if any string was found. This allows
* the string to be deleted if within a batch file.
*
*/
TCHAR *MSEnvVar(errjmp, str, supdate, delim)
jmp_buf *errjmp ;
TCHAR *str ;
int *supdate ;
TCHAR delim ;
{
TCHAR *w0 ; /* Points to ending delim */
TCHAR *w1 ; /* Will hold ptr to env var value */
TCHAR *w2 ;
TCHAR *wSrch ; /* Will hold ptr to search string */
TCHAR *wRepl ; /* Will hold ptr to replace string */
TCHAR savec ;
int noff, nlen, nsrchlen, nrepllen, ndeltalen, fPrefixMatch;
*supdate = 0 ; /* M026 - Init to "Not found" */
for (w0 = str ; *w0 ; w0++) /* Search for ending delim */
if (fEnableExtensions) {
if (*w0 == delim ||
(*w0 == COLON && w0[1] != delim)
// || !_istalnum(*w0)
)
break;
}
else
if (*w0 == delim)
break ;
DEBUG((PAGRP, LFLVL, "MSENVVAR: *w0 = %04x", *w0)) ;
/* M026 - Now check for two together and terminate here to avoid an
* environment search operation
*/
if (!*w0 || (w0 - str) == 0) /* If none or two together "%%" */
return(NULL) ; /* Say, "Not found" */
savec = *w0;
*w0 = NULLC ; /* Null term any Env Var name */
*supdate = mystrlen(str) ; /* M026 - supdate = source len */
if (savec == delim)
*supdate += 1 ; /* Kick it past ending delim */
DEBUG((PAGRP, LFLVL, "MSENVVAR: Possible env var = `%ws'", str)) ;
w1 = GetEnvVar(str) ; /* w1 == NULL or env variable */
if (fEnableExtensions && w1 == NULL) {
if (!_tcsicmp(str, ErrStr))
wsprintf( w1 = LastRetCodeStr, TEXT("%d"), LastRetCode );
else
if (!_tcsicmp(str, TEXT("CMDCMDLINE")))
w1 = GetCommandLine();
}
*w0 = savec ; /* Restore str and... */
if (savec == delim || savec == COLON)
w0 += 1;
//
// If Command Extensions are enabled, then we support munging the
// output of environment variable expansion. Here is the supported
// syntax, all keyed off a trailing COLON character at the end of
// the environment variable name. Note, that %FOO:% is treated
// as it was before.
//
// Environment variable substitution has been enhanced as follows:
//
// %PATH:str1=str2%
//
// would expand the PATH environment variable, substituting each
// occurrence of "str1" in the expanded result with "str2". "str2" can
// be the empty string to effectively delete all occurrences of "str1"
// from the expanded output. Additionally:
//
// %PATH:~10,5%
//
// would expand the PATH environment variable, and then use only the 5
// characters that begin at the 11th character of the expanded result.
// If the ,5 is left off, it will take the entire remainder of the
// expanded result.
//
if (fEnableExtensions && savec == COLON && w1 != NULL) {
wSrch = w0;
if (*w0 == EQI) {
//
// %PATH:~10,5%
w0 += 1;
noff = _tcstol(w0, &w0, 0);
if (*w0 == COMMA) {
w0 += 1;
nlen = _tcstol(w0, &w0, 0);
if (nlen>0) {
//
// If length specified, use it
//
_tcsncpy(w1, w1+noff, nlen);
w1[nlen] = NULLC;
}
} else
//
// Otherwise just copy from offset to end of string.
//
_tcscpy(w1, w1+noff);
if (*w0 == delim)
w0 += 1;
} else {
//
// Not extracting a string, so must be search and replacing
//
// %PATH:str1=str2%
//
//
while (*w0 && *w0 != EQ) {
if (*w0 == delim)
break;
w0 += 1;
}
if (*w0 == EQ) {
*w0++ = NULLC;
nsrchlen = _tcslen(wSrch);
wRepl = w0;
while (*w0 && *w0 != delim)
w0 += 1;
if (*w0) {
*w0 = NULLC;
nrepllen = _tcslen(wRepl);
w2 = w1;
if (*wSrch == STAR) {
fPrefixMatch = TRUE;
wSrch += 1;
nsrchlen -= 1;
} else
fPrefixMatch = FALSE;
while (*w2) {
if (!_tcsnicmp(w2, wSrch, nsrchlen)) {
if (fPrefixMatch) {
nsrchlen = (w2 - w1) + nsrchlen;
w2 = w1;
}
ndeltalen = nsrchlen-nrepllen;
if (ndeltalen < 0)
memmove(w2-ndeltalen, w2, (_tcslen(w2)+1)*sizeof(TCHAR));
else
if (ndeltalen > 0)
_tcscpy(w2, w2+ndeltalen);
memmove(w2, wRepl, nrepllen*sizeof(TCHAR));
if (fPrefixMatch) {
wSrch -= 1;
break;
}
w2 += nrepllen;
} else {
w2 += 1;
}
}
*w0++ = delim;
}
*--wRepl = EQ;
}
else
if (*w0) {
if (errjmp != NULL) {
PutStdErr(MSG_SYNERR_GENL, ONEARG, wSrch);
longjmp(*errjmp,-1) ;
} else
return NULL;
}
}
*supdate += w0 - wSrch + 1;
}
return(w1) ; /* ...return what was found */
}
/*** MSCmdVar - Does command variable substitution
*
* Purpose:
* When percent signs are found in the newly filled lexer buffer,
* this function is called to determine if there is a command processor
* variable substitution possible.
*
* TCHAR *MSCmdVar(TCHAR *srcp, int *supdate, TCHAR *vars, TCHAR *subs[])
*
* Args:
* errjmp - optional pointer to jmp_buf for errors
* srcp - pointer to a possible variable name
* supdate - location to place variable name length in
* vars - array of character variable names to look for
* subs - array of substitution strings for each variable name.
*
* Returns:
* If there is no ending delimiter
* set supdate to 0
* return NULL
* else
* set supdate to the enclosed string length
* if the string is not a variable
* return NULL
* else
* return a pointer to the replacement string
*/
TCHAR *MSCmdVar(errjmp, srcp, supdate, vars, subs)
jmp_buf *errjmp ;
TCHAR *srcp ;
int *supdate ;
TCHAR *vars ;
TCHAR *subs[] ;
{
TCHAR *substr;
TCHAR *s1;
int j;
substr = NULL;
*supdate = 0;
//
// If extensions are enabled, we support the following syntax for expanding
// variable values:
// %~fi - expands %i to a fully qualified path name
// %~di - expands %i to a drive letter only
// %~pi - expands %i to a path only
// %~ni - expands %i to a file name only
// %~xi - expands %i to a file extension only
// %~si - changes the meaning of n and x options to
// reference the short name instead
// %~$PATH:i - searches the directories listed in the PATH
// environment variable and expands %i to the
// fully qualified name of the first one found.
// If the environment variable name is not
// defined or the file is not found by the
// search, then this modifier expands to the
// empty string
//
// The modifiers can be combined to get compound results:
//
// %~dpi - expands %i to a drive letter and path only
// %~nxi - expands %i to a file name and extension only
// %~dp$PATH:i - searches the directories listed in the PATH
// environment variable for %i and expands to the
// drive letter and path of the first one found.
//
//
// See if new syntax is being specified
//
if (fEnableExtensions && *srcp == EQI) {
BOOL fWantFullPath = FALSE;
BOOL fWantDrive = FALSE;
BOOL fWantPath = FALSE;
BOOL fWantName = FALSE;
BOOL fWantExtension = FALSE;
BOOL fWantShortName = FALSE;
WIN32_FIND_DATA FindBuf;
HANDLE FindHandle;
TCHAR c;
TCHAR ArgStr[MAX_PATH];
TCHAR FullPath[MAX_PATH], NullExt;
TCHAR *FilePart, *Extension, *StartPath, *VarName, *SearchVar, *StartBuf;
DWORD FullPathLength;
//
// New syntax. Scan the modifiers after the ~ until we see
// a single letter variable name that matches one the passed in
// array of variable letters.
//
FullPathLength = 0;
SearchVar = NULL;
StartBuf = srcp-1;
s1 = NULL;
while (!s1 && *++srcp) {
switch (_totlower(*srcp)) {
case TEXT('f'): fWantFullPath = TRUE; break;
case TEXT('d'): fWantDrive = TRUE; break;
case TEXT('p'): fWantPath = TRUE; break;
case TEXT('n'): fWantName = TRUE; break;
case TEXT('x'): fWantExtension = TRUE; break;
case TEXT('s'): fWantShortName = TRUE; break;
case TEXT('$'): VarName = ++srcp;
while (*srcp && *srcp != COLON)
srcp++;
if (!*srcp) {
if (errjmp != NULL) {
PutStdErr(MSG_PATH_OPERATOR_INVALID, ONEARG, StartBuf) ;
longjmp(*errjmp,-1) ;
} else
return NULL;
}
*srcp = NULLC;
SearchVar = MyGetEnvVarPtr(VarName);
if (SearchVar == NULL) {
SearchVar = (TCHAR *)-1;
}
*srcp = COLON;
break;
default:
if ((s1 = _tcsrchr(vars, *srcp)) == NULL) {
if (errjmp != NULL) {
PutStdErr(MSG_PATH_OPERATOR_INVALID, ONEARG, StartBuf) ;
longjmp(*errjmp,-1) ;
} else
return NULL;
}
}
}
//
// If we get here without a variable name, then bogus input.
// Bail with an error message.
//
if (s1 == NULL) {
if (errjmp != NULL) {
PutStdErr(MSG_PATH_OPERATOR_INVALID, ONEARG, StartBuf) ;
longjmp(*errjmp,-1) ;
} else
return NULL;
}
//
// Get current value of variable
//
substr = subs[s1 - vars];
if (substr != NULL && *substr == QUOTE) {
_tcscpy(ArgStr, substr+1);
s1 = lastc(ArgStr);
if (*s1 == QUOTE)
*s1 = NULLC;
substr = ArgStr;
}
else
if (substr != NULL &&
*srcp == TEXT('0') &&
CurBat != NULL &&
CurBat->orgaptr0 == substr &&
SearchVar == NULL &&
(fWantFullPath || fWantDrive || fWantPath || fWantName ||
fWantExtension || fWantShortName)
) {
substr = CurBat->filespec;
}
// Skip past the variable name letter and tell caller how much of the
// source string we consumed.
//
++srcp ;
*supdate = (srcp - StartBuf) - 1;
//
// If the variable has a value, then apply the modifiers to the
// value.
//
if (substr && *substr) {
//
// If requested searching an environment variable path, do that.
//
FullPath[0] = NULLC;
if (SearchVar != NULL) {
if (SearchVar != (TCHAR *)-1) {
FullPathLength = SearchPath(SearchVar,substr,NULL,MAX_PATH,FullPath,&FilePart);
if (FullPathLength == 0)
SearchVar = (TCHAR *)-1;
else
if (!fWantFullPath && !fWantDrive &&
!fWantPath && !fWantName && !fWantExtension)
fWantFullPath = TRUE;
}
}
if (SearchVar == NULL) {
//
// If not searching environment variable path, start with full path.
FullPathLength = GetFullPathName(substr,MAX_PATH,FullPath,&FilePart);
if (FilePart == NULL)
FilePart = _tcschr( FullPath, NULLC );
}
else
if (SearchVar == (TCHAR *)-1) {
//
// If search of environment variable path failed, result is empty string
//
substr = NULL;
}
//
// Fixup the path to have same case as on disk, substituting short
// names if requested.
//
FixupPath(FullPath, fWantShortName);
//
// If we have a full path, the result gets the portions requested by
// the user, unless they wanted the full path, in which case there is
// nothing more to do.
//
if (FullPathLength != 0) {
if (!fWantFullPath) {
StartPath = FullPath + 2;
if (!fWantDrive) {
StartPath = _tcscpy(FullPath, StartPath);
FilePart -= 2;
}
if (!fWantPath)
FilePart = _tcscpy(StartPath, FilePart);
Extension = _tcsrchr(FilePart, DOT);
if (Extension == NULL) {
NullExt = NULLC;
Extension = &NullExt;
}
if (!fWantExtension)
*Extension = NULLC;
if (!fWantName)
_tcscpy(FilePart, Extension);
}
substr = FullPath;
}
}
}
else
if (*srcp && (s1 = _tcsrchr(vars, *srcp))) {
//
// Old syntax. Result is value of variable
//
substr = subs[s1 - vars]; /* Found variable*/
//
// Skip past the variable name letter and tell caller how much of the
// source string we consumed.
//
++srcp ; /* Kick past name*/
*supdate += 1;
} ;
//
// If result was empty, then return the null string. Otherwise return the result
//
if (!substr && *supdate != 0)
return TEXT("");
else
return substr;
}