1008 lines
32 KiB
C
1008 lines
32 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1988-1999 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
cinit.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Initialization
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "cmd.h"
|
||
|
|
||
|
#if CMD_DEBUG_ENABLE
|
||
|
unsigned DebGroup=0;
|
||
|
unsigned DebLevel=0;
|
||
|
#endif
|
||
|
|
||
|
TCHAR CurDrvDir[MAX_PATH] ; /* Current drive and directory */
|
||
|
BOOL SingleBatchInvocation = FALSE ;
|
||
|
BOOL SingleCommandInvocation = FALSE;
|
||
|
BOOLEAN fDisableUNCCheck = FALSE;
|
||
|
int cmdfound = -1; /* @@5 - command found index */
|
||
|
int cpyfirst = TRUE; /* @@5 - flag to ctrl DOSQFILEMODE calls */
|
||
|
int cpydflag = FALSE; /* @@5 - flag save dirflag from las DQFMDE*/
|
||
|
int cpydest = FALSE; /* @@6 - flag to not display bad dev msg */
|
||
|
int cdevfail = FALSE; /* @@7 - flag to not display extra emsg */
|
||
|
#ifdef UNICODE
|
||
|
BOOLEAN fOutputUnicode = FALSE;/* Unicode/Ansi output */
|
||
|
#endif // UNICODE
|
||
|
|
||
|
BOOLEAN fEnableExtensions = FALSE;
|
||
|
BOOLEAN fDefaultExtensions = TRUE;
|
||
|
BOOLEAN fDelayedExpansion = FALSE;
|
||
|
|
||
|
BOOLEAN ReportDelayLoadErrors = TRUE;
|
||
|
|
||
|
unsigned tywild = 0; /* flag to tell if wild type args @@5 @J1 */
|
||
|
int array_size = 0 ; /* original array size is zero */
|
||
|
CPINFO CurrentCPInfo;
|
||
|
UINT CurrentCP;
|
||
|
|
||
|
WORD wDefaultColor = 0; // default is whatever console currently has
|
||
|
// but default can be overriden by the registry.
|
||
|
TCHAR chCompletionCtrl = SPACE; // Default is no completion (must be Ctrl character)
|
||
|
TCHAR chPathCompletionCtrl = SPACE;
|
||
|
|
||
|
|
||
|
|
||
|
VOID InitLocale( VOID );
|
||
|
|
||
|
extern TCHAR ComSpec[], ComSpecStr[] ; /* M021 */
|
||
|
extern TCHAR PathStr[], PCSwitch, SCSwitch, PromptStr[] ;
|
||
|
extern TCHAR PathExtStr[], PathExtDefaultStr[];
|
||
|
extern TCHAR BCSwitch ; /* @@ */
|
||
|
extern TCHAR QCSwitch ; /* @@dv */
|
||
|
extern TCHAR UCSwitch;
|
||
|
extern TCHAR ACSwitch;
|
||
|
extern TCHAR XCSwitch;
|
||
|
extern TCHAR YCSwitch;
|
||
|
extern TCHAR DevNul[], VolSrch[] ; /* M021 - To set PathChar */
|
||
|
|
||
|
extern TCHAR SwitChar, PathChar ; /* M000 - Made non-settable */
|
||
|
extern int Necho ; /* @@dv - true if /Q for no echo */
|
||
|
|
||
|
extern TCHAR MsgBuf[];
|
||
|
|
||
|
extern struct envdata CmdEnv;
|
||
|
extern struct envdata * penvOrig;
|
||
|
struct envdata OrigEnv;
|
||
|
|
||
|
extern TCHAR TmpBuf[] ; /* M034 */
|
||
|
|
||
|
extern TCHAR ComSpec[];
|
||
|
|
||
|
TCHAR *CmdSpec = &ComSpec[1]; /* M033 */
|
||
|
|
||
|
extern unsigned DosErr ; /* D64 */
|
||
|
|
||
|
//
|
||
|
// TRUE if the ctrl-c thread has been run.
|
||
|
//
|
||
|
BOOL CtrlCSeen;
|
||
|
|
||
|
//
|
||
|
// Set TRUE when it is ok the print a control-c.
|
||
|
// If we are waiting for another process this will be
|
||
|
// FALSE
|
||
|
BOOLEAN fPrintCtrlC = TRUE;
|
||
|
|
||
|
//
|
||
|
// console mode at program startup time. Used to reset mode
|
||
|
// after running another process.
|
||
|
//
|
||
|
DWORD dwCurInputConMode;
|
||
|
DWORD dwCurOutputConMode;
|
||
|
|
||
|
//
|
||
|
// Initial Title. Used for restoration on abort etc.
|
||
|
// MAX_PATH was arbitrary
|
||
|
//
|
||
|
PTCHAR pszTitleCur;
|
||
|
PTCHAR pszTitleOrg;
|
||
|
BOOLEAN fTitleChanged = FALSE; // title has been changed and needs to be reset
|
||
|
|
||
|
//
|
||
|
// used to gate access to ctrlcseen flag between ctrl-c thread
|
||
|
// and main thread
|
||
|
//
|
||
|
CRITICAL_SECTION CtrlCSection;
|
||
|
LPCRITICAL_SECTION lpcritCtrlC;
|
||
|
|
||
|
//
|
||
|
// Used to set and reset ctlcseen flag
|
||
|
//
|
||
|
VOID SetCtrlC();
|
||
|
VOID ResetCtrlC();
|
||
|
|
||
|
Handler(
|
||
|
IN ULONG CtrlType
|
||
|
)
|
||
|
{
|
||
|
if ( (CtrlType == CTRL_C_EVENT) ||
|
||
|
(CtrlType == CTRL_BREAK_EVENT) ) {
|
||
|
|
||
|
//
|
||
|
// Note that we had a ^C event
|
||
|
//
|
||
|
|
||
|
SetCtrlC();
|
||
|
|
||
|
//
|
||
|
// Display the ^C if we are enabled and if we're not in a batch file
|
||
|
//
|
||
|
|
||
|
if (fPrintCtrlC && CurrentBatchFile != NULL) {
|
||
|
|
||
|
fprintf( stderr, "^C" );
|
||
|
fflush( stderr );
|
||
|
|
||
|
}
|
||
|
return TRUE;
|
||
|
} else {
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/********************* START OF SPECIFICATION **************************/
|
||
|
/* */
|
||
|
/* SUBROUTINE NAME: Init */
|
||
|
/* */
|
||
|
/* DESCRIPTIVE NAME: CMD.EXE Initialization Process */
|
||
|
/* */
|
||
|
/* FUNCTION: Initialization of CMD.EXE. */
|
||
|
/* */
|
||
|
/* NOTES: */
|
||
|
/* */
|
||
|
/* ENTRY POINT: Init */
|
||
|
/* */
|
||
|
/* INPUT: None. */
|
||
|
/* */
|
||
|
/* OUTPUT: None. */
|
||
|
/* */
|
||
|
/* EXIT-NORMAL: */
|
||
|
/* Return the pointer to command line. */
|
||
|
/* */
|
||
|
/* EXIT-ERROR: */
|
||
|
/* Return NULL string. */
|
||
|
/* */
|
||
|
/* EFFECTS: None. */
|
||
|
/* */
|
||
|
/********************** END OF SPECIFICATION **************************/
|
||
|
/*** Init - initialize Command
|
||
|
*
|
||
|
* Purpose:
|
||
|
* Save current SIGINTR response (SIGIGN or SIGDEF) and set SIGIGN.
|
||
|
* If debugging
|
||
|
* Set DebGroup & DebLevel
|
||
|
* Get Environment and init CmdEnv structure (M034)
|
||
|
* Check for any switches.
|
||
|
* Make a version check.
|
||
|
* If version out of range
|
||
|
* Print error message.
|
||
|
* If Permanent Command
|
||
|
* Loop forever
|
||
|
* Else
|
||
|
* Exit.
|
||
|
* Save the current drive and directory.
|
||
|
* Check for other command line arguments.
|
||
|
* Set up the environment.
|
||
|
* Always print a bannner if MSDOS version of Command.
|
||
|
* Return any "comline" value found.
|
||
|
*
|
||
|
* TCHAR *Init()
|
||
|
*
|
||
|
* Args:
|
||
|
*
|
||
|
* Returns:
|
||
|
* Comline (it's NULL if NOT in single command mode).
|
||
|
*
|
||
|
* Notes:
|
||
|
* See CSIG.C for a description of the way ^Cs and INT24s are handled
|
||
|
* during initialization.
|
||
|
* M024 - Brought functionality for checking non-specific args into
|
||
|
* init from routines CheckOtherArgs and ChangeComSpec which have
|
||
|
* been eliminated.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
BOOL Init(
|
||
|
TCHAR *InitialCmds[]
|
||
|
)
|
||
|
{
|
||
|
#if 0 /* Set debug group and level words */
|
||
|
|
||
|
int fh;
|
||
|
PTCHAR nptr;
|
||
|
|
||
|
nptr = TmpBuf;
|
||
|
nptr = EatWS(nptr, NULL);
|
||
|
nptr = mystrchr(nptr, TEXT(' '));
|
||
|
nptr = EatWS(nptr, NULL);
|
||
|
|
||
|
//
|
||
|
// Assume a non-zero debugging group
|
||
|
//
|
||
|
DebGroup = hstoi(nptr) ; /* 1st debug arg */
|
||
|
if (DebGroup) {
|
||
|
for (fh=0 ; fh < 2 ; fh++) {
|
||
|
if (fh == 1)
|
||
|
DebLevel = hstoi(nptr) ; /* 2nd debug arg */
|
||
|
while(*nptr && !_istspace(*nptr)) { /* Index past it */
|
||
|
++nptr ;
|
||
|
}
|
||
|
nptr = EatWS(nptr, NULL) ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DEBUG((INGRP, RSLVL, "INIT: Debug GRP=%04x LVL=%04x", DebGroup, DebLevel)) ;
|
||
|
mystrcpy(TmpBuf, nptr) ; /* Elim from cmdline */
|
||
|
#endif
|
||
|
//
|
||
|
// Initialize Critical Section to handle access to
|
||
|
// flag for control C handling
|
||
|
//
|
||
|
lpcritCtrlC = &CtrlCSection;
|
||
|
InitializeCriticalSection(lpcritCtrlC);
|
||
|
ResetCtrlC();
|
||
|
|
||
|
SetConsoleCtrlHandler(Handler,TRUE);
|
||
|
|
||
|
//
|
||
|
// Make sure we have the correct console modes.
|
||
|
//
|
||
|
ResetConsoleMode();
|
||
|
|
||
|
#ifndef UNICODE
|
||
|
setbuf(stdout, NULL); /* Don't buffer output @@5 */
|
||
|
setbuf(stderr, NULL); /* @@5 */
|
||
|
_setmode(1, O_BINARY); /* Set output to text mode @@5 */
|
||
|
_setmode(2, O_BINARY); /* @@5 */
|
||
|
#endif
|
||
|
|
||
|
CmdEnv.handle = GetEnvironmentStrings();
|
||
|
|
||
|
GetRegistryValues(InitialCmds);
|
||
|
|
||
|
mystrcpy(TmpBuf, GetCommandLine());
|
||
|
LexCopy( TmpBuf, TmpBuf, mystrlen( TmpBuf ) ); /* convert dbcs spaces */
|
||
|
|
||
|
GetDir(CurDrvDir, GD_DEFAULT) ;
|
||
|
|
||
|
SetUpEnvironment() ;
|
||
|
|
||
|
/* Check cmdline switches */
|
||
|
CheckSwitches(InitialCmds, TmpBuf);
|
||
|
|
||
|
if (CurDrvDir[0] == BSLASH && CurDrvDir[1] == BSLASH) {
|
||
|
#if 0
|
||
|
if (fEnableExtensions) {
|
||
|
struct cmdnode *n ;
|
||
|
|
||
|
PutStdErr(MSG_SIM_UNC_CURDIR, ONEARG, CurDrvDir);
|
||
|
n = mkstr(sizeof(*n));
|
||
|
n->argptr = mkstr((_tcslen(CurDrvDir)+1) * sizeof(TCHAR));
|
||
|
_tcscpy(n->argptr, CurDrvDir);
|
||
|
GetWindowsDirectory(CurDrvDir, sizeof(CurDrvDir)/sizeof(TCHAR));
|
||
|
ChangeDir2(CurDrvDir, TRUE);
|
||
|
ePushDir(n);
|
||
|
} else
|
||
|
#endif
|
||
|
if (!fDisableUNCCheck) {
|
||
|
PutStdErr(MSG_NO_UNC_INITDIR, ONEARG, CurDrvDir);
|
||
|
GetWindowsDirectory(CurDrvDir, sizeof(CurDrvDir)/sizeof(TCHAR));
|
||
|
ChangeDir2(CurDrvDir, TRUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get current CodePage Info. We need this to decide whether
|
||
|
// or not to use half-width characters. This is actually here
|
||
|
// in the init code for safety - the Dir command calls it before
|
||
|
// each dir is executed, because chcp may have been executed.
|
||
|
//
|
||
|
GetCPInfo((CurrentCP=GetConsoleOutputCP()), &CurrentCPInfo);
|
||
|
|
||
|
InitLocale();
|
||
|
|
||
|
pszTitleCur = HeapAlloc(GetProcessHeap(), 0, MAX_PATH*sizeof(TCHAR) + 2*sizeof(TCHAR));
|
||
|
pszTitleOrg = HeapAlloc(GetProcessHeap(), 0, MAX_PATH*sizeof(TCHAR) + 2*sizeof(TCHAR));
|
||
|
if ((pszTitleCur != NULL) && (pszTitleOrg != NULL)) {
|
||
|
|
||
|
if (GetConsoleTitle(pszTitleOrg, MAX_PATH)) {
|
||
|
mystrcpy(pszTitleCur, pszTitleOrg);
|
||
|
} else {
|
||
|
*pszTitleCur = 0;
|
||
|
*pszTitleOrg = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!SingleCommandInvocation) {
|
||
|
if (FileIsConsole(STDOUT)) {
|
||
|
#ifndef WIN95_CMD
|
||
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||
|
|
||
|
if (!wDefaultColor) {
|
||
|
if (GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
|
||
|
wDefaultColor = csbi.wAttributes;
|
||
|
}
|
||
|
}
|
||
|
#endif // WIN95_CMD
|
||
|
if (wDefaultColor) {
|
||
|
SetColor( wDefaultColor );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Print banner if no command string on command line */
|
||
|
if (!InitialCmds[2]) {
|
||
|
TCHAR VersionFormat[32];
|
||
|
|
||
|
GetVersionString( VersionFormat, sizeof( VersionFormat ) / sizeof( VersionFormat[0] ));
|
||
|
|
||
|
PutStdOut( MSG_MS_DOS_VERSION,
|
||
|
ONEARG,
|
||
|
VersionFormat );
|
||
|
|
||
|
cmd_printf( CrLf );
|
||
|
|
||
|
PutStdOut( MSG_COPYRIGHT, NOARGS ) ;
|
||
|
|
||
|
if (fDefaultExtensions) {
|
||
|
//
|
||
|
// DaveC says say nothing to user here.
|
||
|
//
|
||
|
// PutStdOut(MSG_EXT_ENABLED_BY_DEFAULT, NOARGS);
|
||
|
} else
|
||
|
if (fEnableExtensions) {
|
||
|
PutStdOut(MSG_EXT_ENABLED, NOARGS) ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DEBUG((INGRP, RSLVL, "INIT: Returning now.")) ;
|
||
|
|
||
|
#ifndef WIN95_CMD
|
||
|
{
|
||
|
hKernel32 = GetModuleHandle( TEXT("KERNEL32.DLL") );
|
||
|
lpCopyFileExW = (LPCOPYFILEEX_ROUTINE)
|
||
|
GetProcAddress( hKernel32, "CopyFileExW" );
|
||
|
|
||
|
lpIsDebuggerPresent = (LPISDEBUGGERPRESENT_ROUTINE)
|
||
|
GetProcAddress( hKernel32, "IsDebuggerPresent" );
|
||
|
|
||
|
lpSetConsoleInputExeName = (LPSETCONSOLEINPUTEXENAME_ROUTINE)
|
||
|
GetProcAddress( hKernel32, "SetConsoleInputExeNameW" );
|
||
|
}
|
||
|
|
||
|
#endif // WIN95_CMD
|
||
|
|
||
|
return(InitialCmds[0] != NULL || InitialCmds[1] != NULL || InitialCmds[2] != NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
void GetRegistryValues(
|
||
|
TCHAR *InitialCmds[]
|
||
|
)
|
||
|
{
|
||
|
long rc;
|
||
|
HKEY hKey;
|
||
|
ULONG ValueBuffer[ 1024 ];
|
||
|
LPBYTE lpData;
|
||
|
DWORD cbData;
|
||
|
DWORD dwType;
|
||
|
DWORD cchSrc, cchDst;
|
||
|
PTCHAR s;
|
||
|
int i;
|
||
|
HKEY PredefinedKeys[2] = {HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER};
|
||
|
|
||
|
if (fDefaultExtensions) {
|
||
|
fEnableExtensions = TRUE;
|
||
|
}
|
||
|
|
||
|
for (i=0; i<2; i++) {
|
||
|
rc = RegOpenKey(PredefinedKeys[i], TEXT("Software\\Microsoft\\Command Processor"), &hKey);
|
||
|
if (rc) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
dwType = REG_NONE;
|
||
|
lpData = (LPBYTE)ValueBuffer;
|
||
|
cbData = sizeof(ValueBuffer);
|
||
|
rc = RegQueryValueEx(hKey, TEXT("DisableUNCCheck"), NULL, &dwType, lpData, &cbData);
|
||
|
if (!rc) {
|
||
|
if (dwType == REG_DWORD) {
|
||
|
fDisableUNCCheck = (BOOLEAN)(*(PULONG)lpData != 0);
|
||
|
}
|
||
|
else
|
||
|
if (dwType == REG_SZ) {
|
||
|
fDisableUNCCheck = (BOOLEAN)(_wtol((PWSTR)lpData) == 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cbData = sizeof(ValueBuffer);
|
||
|
rc = RegQueryValueEx(hKey, TEXT("EnableExtensions"), NULL, &dwType, lpData, &cbData);
|
||
|
if (!rc) {
|
||
|
if (dwType == REG_DWORD) {
|
||
|
fEnableExtensions = (BOOLEAN)(*(PULONG)lpData != 0);
|
||
|
}
|
||
|
else
|
||
|
if (dwType == REG_SZ) {
|
||
|
fEnableExtensions = (BOOLEAN)(_wtol((PWSTR)lpData) == 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cbData = sizeof(ValueBuffer);
|
||
|
rc = RegQueryValueEx(hKey, TEXT("DelayedExpansion"), NULL, &dwType, lpData, &cbData);
|
||
|
if (!rc) {
|
||
|
if (dwType == REG_DWORD) {
|
||
|
fDelayedExpansion = (BOOLEAN)(*(PULONG)lpData != 0);
|
||
|
}
|
||
|
else
|
||
|
if (dwType == REG_SZ) {
|
||
|
fDelayedExpansion = (BOOLEAN)(_wtol((PWSTR)lpData) == 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cbData = sizeof(ValueBuffer);
|
||
|
rc = RegQueryValueEx(hKey, TEXT("DefaultColor"), NULL, &dwType, lpData, &cbData);
|
||
|
if (!rc) {
|
||
|
if (dwType == REG_DWORD) {
|
||
|
wDefaultColor = (WORD) *(PULONG)lpData;
|
||
|
}
|
||
|
else
|
||
|
if (dwType == REG_SZ) {
|
||
|
wDefaultColor = (WORD)_tcstol((PTCHAR)lpData, NULL, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cbData = sizeof(ValueBuffer);
|
||
|
rc = RegQueryValueEx(hKey, TEXT("CompletionChar"), NULL, &dwType, lpData, &cbData);
|
||
|
if (!rc) {
|
||
|
if (dwType == REG_DWORD) {
|
||
|
chCompletionCtrl = (TCHAR)*(PULONG)lpData;
|
||
|
}
|
||
|
else
|
||
|
if (dwType == REG_SZ) {
|
||
|
chCompletionCtrl = (TCHAR)_tcstol((PTCHAR)lpData, NULL, 0);
|
||
|
}
|
||
|
|
||
|
if (chCompletionCtrl == 0 || chCompletionCtrl == 0x0d || chCompletionCtrl > SPACE) {
|
||
|
chCompletionCtrl = SPACE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cbData = sizeof(ValueBuffer);
|
||
|
rc = RegQueryValueEx(hKey, TEXT("PathCompletionChar"), NULL, &dwType, lpData, &cbData);
|
||
|
if (!rc) {
|
||
|
if (dwType == REG_DWORD) {
|
||
|
chPathCompletionCtrl = (TCHAR)*(PULONG)lpData;
|
||
|
}
|
||
|
else
|
||
|
if (dwType == REG_SZ) {
|
||
|
chPathCompletionCtrl = (TCHAR)_tcstol((PTCHAR)lpData, NULL, 0);
|
||
|
}
|
||
|
|
||
|
if (chPathCompletionCtrl == 0 || chPathCompletionCtrl == 0x0d || chPathCompletionCtrl > SPACE) {
|
||
|
chPathCompletionCtrl = SPACE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (chCompletionCtrl == SPACE && chPathCompletionCtrl < SPACE) {
|
||
|
chCompletionCtrl = chPathCompletionCtrl;
|
||
|
} else
|
||
|
if (chPathCompletionCtrl == SPACE && chCompletionCtrl < SPACE) {
|
||
|
chPathCompletionCtrl = chCompletionCtrl;
|
||
|
}
|
||
|
|
||
|
cbData = sizeof(ValueBuffer);
|
||
|
rc = RegQueryValueEx(hKey, TEXT("AutoRun"), NULL, &dwType, lpData, &cbData);
|
||
|
if (!rc) {
|
||
|
s = (TCHAR *)lpData;
|
||
|
if (dwType == REG_EXPAND_SZ) {
|
||
|
cchSrc = cbData / sizeof( TCHAR );
|
||
|
cchDst = (sizeof( ValueBuffer ) - cbData) / sizeof( TCHAR );
|
||
|
if (ExpandEnvironmentStrings( s,
|
||
|
&s[ cchSrc+2 ],
|
||
|
cchDst
|
||
|
)
|
||
|
)
|
||
|
_tcscpy( s, &s[ cchSrc+2 ] );
|
||
|
else
|
||
|
*s = NULLC;
|
||
|
}
|
||
|
|
||
|
if (*s)
|
||
|
InitialCmds[i] = mystrcpy( mkstr( (_tcslen(s)+1) * sizeof( TCHAR ) ), s );
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize for %RANDOM%
|
||
|
//
|
||
|
srand( (unsigned)time( NULL ) );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*** CheckSwitches - process Command's switches
|
||
|
*
|
||
|
* Purpose:
|
||
|
* Check to see if Command was passed any switches and take appropriate
|
||
|
* action. The switches are:
|
||
|
* /P - Permanent Command. Set permanent CMD flag.
|
||
|
* /C - Single command. Build a command line out of the rest of
|
||
|
* the args and pass it back to Init.
|
||
|
* @@ /K - Same as /C but also set BatCom flag.
|
||
|
* /Q - No echo
|
||
|
* /A - Output in ANSI
|
||
|
* /U - Output in UNICODE
|
||
|
*
|
||
|
* All other switches are ignored.
|
||
|
*
|
||
|
* TCHAR *CheckSwitches(TCHAR *nptr)
|
||
|
*
|
||
|
* Args:
|
||
|
* nptr = Ptr to cmdline to check for switches
|
||
|
*
|
||
|
* Returns:
|
||
|
* Comline (it's NULL if NOT in single command mode).
|
||
|
*
|
||
|
* Notes:
|
||
|
* M034 - This function revised to use the raw cmdline
|
||
|
* from the passed environment.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void
|
||
|
CheckSwitches(
|
||
|
TCHAR *InitialCmds[],
|
||
|
TCHAR *nptr
|
||
|
)
|
||
|
{
|
||
|
TCHAR a, /* Holds switch value */
|
||
|
*comline = NULL , /* Ptr to command line if /c found */
|
||
|
store,
|
||
|
*ptr, /* A temporary pointers */
|
||
|
*ptr_b,
|
||
|
*ptr_e;
|
||
|
|
||
|
BOOLEAN fAutoGen = FALSE; // On if "/S" in cmdline meaning cmdline was parsed by CMD.EXE previously
|
||
|
BOOLEAN fOrigEnableExt;
|
||
|
struct cmdnode cmd_node; // need for SearchForExecutable()
|
||
|
TCHAR cmdline [MAX_PATH];
|
||
|
TCHAR argptr [MAX_PATH];
|
||
|
|
||
|
TCHAR CmdBuf [MAXTOKLEN+3];
|
||
|
int retc;
|
||
|
|
||
|
memset( &cmd_node, 0, sizeof( cmd_node ));
|
||
|
|
||
|
fOrigEnableExt = fEnableExtensions;
|
||
|
DEBUG((INGRP, ACLVL, "CHKSW: entered.")) ;
|
||
|
|
||
|
while (nptr = mystrchr(nptr, SwitChar)) {
|
||
|
a = (TCHAR) _totlower(nptr[1]) ;
|
||
|
|
||
|
if (a == NULLC)
|
||
|
break;
|
||
|
|
||
|
if (a == QMARK) {
|
||
|
|
||
|
#define CTRLCBREAK if (CtrlCSeen) break
|
||
|
BeginHelpPause();
|
||
|
do {
|
||
|
CTRLCBREAK; PutStdOut(MSG_HELP_CMD, NOARGS);
|
||
|
CTRLCBREAK; PutStdOut(MSG_HELP_CMD1, NOARGS);
|
||
|
|
||
|
if (!fOrigEnableExt && !fEnableExtensions) break;
|
||
|
|
||
|
CTRLCBREAK; PutStdOut(MSG_HELP_CMD_EXTENSIONS, NOARGS);
|
||
|
CTRLCBREAK; PutStdOut(MSG_HELP_CMD_EXTENSIONS1, NOARGS);
|
||
|
CTRLCBREAK; PutStdOut(MSG_HELP_CMD_COMPLETION1, NOARGS);
|
||
|
CTRLCBREAK; PutStdOut(MSG_HELP_CMD_COMPLETION2, NOARGS);
|
||
|
|
||
|
} while ( FALSE );
|
||
|
|
||
|
EndHelpPause();
|
||
|
|
||
|
CMDexit(1);
|
||
|
} else if (a == QCSwitch) { /* Quiet cmd switch */
|
||
|
|
||
|
Necho = TRUE ;
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
|
||
|
} else if ((a == SCSwitch) || (a == BCSwitch) || a == TEXT('r')) {
|
||
|
DEBUG((INGRP, ACLVL, "CHKSW: Single command switch")) ;
|
||
|
|
||
|
if ( a == BCSwitch ) {
|
||
|
SingleBatchInvocation = TRUE; // /K specified
|
||
|
} else {
|
||
|
SingleCommandInvocation = TRUE; // /C or /R specified
|
||
|
}
|
||
|
|
||
|
if (!(comline = mkstr(mystrlen(nptr+2)*sizeof(TCHAR)+2*sizeof(TCHAR)))) {
|
||
|
PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS);
|
||
|
CMDexit(1) ;
|
||
|
} ;
|
||
|
|
||
|
mystrcpy(comline, nptr+2) ; /* Make comline */
|
||
|
|
||
|
*nptr = NULLC ; /* Invalidate this arg */
|
||
|
|
||
|
comline = SkipWhiteSpace( comline );
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------------------
|
||
|
// CMD.EXE uses quotes by two reasons:
|
||
|
// 1. to embed command symbols "&", "<", ">", "|", "&&", "||" into command arguments, e.g.
|
||
|
// cmd /c " dir | more "
|
||
|
// 2. to embed spaces into filename, e.g.
|
||
|
// cmd /c " my batfile with spaces.cmd"
|
||
|
// Note that the caret "^" has no effect when used in between quotes in the current implementation (941221).
|
||
|
// Also, CMD.EXE binds the quote with the next one.
|
||
|
//
|
||
|
// I see a problem here: the commands like
|
||
|
// cmd /c "findstr " | " | find "smth" " OR
|
||
|
// cmd /c "ls | " my filterbat with spaces.cmd" | more"
|
||
|
// WON'T WORK unless we all decide to change CMD's syntax to better handle quotes!
|
||
|
//
|
||
|
// There is more to it: when CMD.EXE parses pipes,CMD creates process with the command argument like this:
|
||
|
// <full path of CMD.EXE> /S /C" <cmdname> "
|
||
|
// so we've got spaces inside the quotes.
|
||
|
//
|
||
|
// I hope I am not missing anything else...
|
||
|
//
|
||
|
// With given design restrictions, I will at least solve simple but most wide-spread problem:
|
||
|
// using filenames with spaces by trying this:
|
||
|
// IF ( (there is no /S switch ) AND // it is not the result of prev. parsing
|
||
|
// (there are exactly 2 quotes) AND // the existing design problem with multiple quotes
|
||
|
// (there is no special chars between quotes) AND // don't break command symbols parsing
|
||
|
// (there is a whitespace between quotes) AND // otherwise it is not filename with spaces
|
||
|
// (the token between quotes is a valid executable) )// otherwise we can't help anyway
|
||
|
// THEN
|
||
|
// Preserve quotes // Assume it is a filename with spaces
|
||
|
// ELSE
|
||
|
// Exec. old logic // Strip first and last quotes
|
||
|
//
|
||
|
// Ugly, but what options do I have? Only to patch existing logic or change syntax.
|
||
|
//-----------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
if (fAutoGen) // seen /S switch
|
||
|
goto old_way;
|
||
|
|
||
|
|
||
|
if (*comline == QUOTE) {
|
||
|
ptr_b = comline + 1;
|
||
|
ptr_e = mystrchr (ptr_b, QUOTE);
|
||
|
if (ptr_e) { // at least 2 quotes
|
||
|
ptr_b = ptr_e + 1;
|
||
|
ptr_e = mystrchr (ptr_b, QUOTE);
|
||
|
if (ptr_e) { // more than 2 quotes
|
||
|
goto old_way;
|
||
|
}
|
||
|
}
|
||
|
else { // only 1 quote
|
||
|
goto old_way;
|
||
|
}
|
||
|
// exactly 2 quotes
|
||
|
store = *ptr_b;
|
||
|
*ptr_b = NULLC;
|
||
|
|
||
|
if ( (mystrchr (comline, ANDOP) ) ||
|
||
|
(mystrchr (comline, INOP) ) ||
|
||
|
(mystrchr (comline, OUTOP) ) ||
|
||
|
(mystrchr (comline, LPOP) ) ||
|
||
|
(mystrchr (comline, RPOP) ) ||
|
||
|
(mystrchr (comline, SILOP) ) ||
|
||
|
(mystrchr (comline, ESCHAR)) ||
|
||
|
(mystrchr (comline, PIPOP) ) ) {
|
||
|
|
||
|
*ptr_b = store; // special chars between quotes
|
||
|
goto old_way;
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( ! mystrchr (comline, TEXT(' ')) ) {
|
||
|
*ptr_b = store; // no spaces between quotes
|
||
|
goto old_way;
|
||
|
}
|
||
|
|
||
|
// the last check is for valid executable
|
||
|
|
||
|
cmd_node.type = CMDTYP ;
|
||
|
cmd_node.cmdline = cmdline;
|
||
|
cmd_node.argptr = argptr;
|
||
|
cmd_node.rio = NULL;
|
||
|
|
||
|
mystrcpy (cmdline, comline); // get token between quotes
|
||
|
mystrcpy (argptr, TEXT (" ") );
|
||
|
|
||
|
*ptr_b = store; // restore comline
|
||
|
|
||
|
retc = SearchForExecutable (&cmd_node, CmdBuf);
|
||
|
if ( ( retc == SFE_NOTFND) || ( retc == SFE_FAIL) )
|
||
|
goto old_way;
|
||
|
|
||
|
goto new_way; // assume filename and DO NOT strip quotes.
|
||
|
}
|
||
|
|
||
|
old_way:
|
||
|
if (*comline == QUOTE) {
|
||
|
++comline ;
|
||
|
ptr = mystrrchr(comline, QUOTE);
|
||
|
if ( ptr ) {
|
||
|
*ptr = NULLC;
|
||
|
++ptr;
|
||
|
mystrcat(comline,ptr);
|
||
|
}
|
||
|
}
|
||
|
new_way:
|
||
|
|
||
|
*(comline+mystrlen(comline)) = NLN ;
|
||
|
|
||
|
DEBUG((INGRP, ACLVL, "CHKSW: Single command line = `%ws'", comline)) ;
|
||
|
InitialCmds[2] = comline;
|
||
|
break ; /* Once /K or /C found, no more args exist */
|
||
|
|
||
|
} else if (a == UCSwitch) { /* Unicode output switch */
|
||
|
#ifdef UNICODE
|
||
|
fOutputUnicode = TRUE;
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
#else
|
||
|
PutStdErr(MSG_UNICODE_NOT_SUPPORTED, NOARGS);
|
||
|
#endif // UNICODE
|
||
|
} else if (a == ACSwitch) { /* Ansi output switch */
|
||
|
#ifdef UNICODE
|
||
|
fOutputUnicode = FALSE;
|
||
|
#endif // UNICODE
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
//
|
||
|
// Old style of enabling extensions with /X
|
||
|
//
|
||
|
} else if (a == XCSwitch) { /* Enable extensions switch */
|
||
|
fEnableExtensions = TRUE;
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
|
||
|
//
|
||
|
// Old style of disabling extensions with /Y
|
||
|
//
|
||
|
} else if (a == YCSwitch) { /* Disable extensions switch */
|
||
|
fEnableExtensions = FALSE;
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
|
||
|
//
|
||
|
// Enable/Disable command extensions. /E or /E:ON to enable
|
||
|
// and /E:OFF to disable.
|
||
|
//
|
||
|
} else if (a == TEXT('e')) {
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
if (*nptr == COLON && !_tcsnicmp(nptr+1, TEXT("OFF"), 3)) {
|
||
|
fEnableExtensions = FALSE;
|
||
|
mystrcpy(nptr, nptr+4) ;
|
||
|
} else {
|
||
|
fEnableExtensions = TRUE;
|
||
|
if (!_tcsnicmp(nptr, TEXT(":ON"), 3)) {
|
||
|
mystrcpy(nptr, nptr+3) ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Disable AutoRun from Registry if /D specified.
|
||
|
//
|
||
|
} else if (a == TEXT('d')) {
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
InitialCmds[0] = NULL;
|
||
|
InitialCmds[1] = NULL;
|
||
|
|
||
|
//
|
||
|
// Enable/Disable file and directory name completion. /F or /F:ON to
|
||
|
// enable and /F:OFF to disable.
|
||
|
//
|
||
|
} else if (a == TEXT('f')) {
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
if (*nptr == COLON && !_tcsnicmp(nptr+1, TEXT("OFF"), 3)) {
|
||
|
chCompletionCtrl = SPACE;
|
||
|
chPathCompletionCtrl = SPACE;
|
||
|
mystrcpy(nptr, nptr+4) ;
|
||
|
} else {
|
||
|
chCompletionCtrl = 0x6; // Ctrl-F
|
||
|
chPathCompletionCtrl = 0x4; // Ctrl-D
|
||
|
if (!_tcsnicmp(nptr, TEXT(":ON"), 3)) {
|
||
|
mystrcpy(nptr, nptr+3) ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Enable/Disable delayed variable expansion inside FOR loops. /V or /V:ON to
|
||
|
// enable and /V:OFF to disable.
|
||
|
//
|
||
|
} else if (a == TEXT('v')) {
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
if (*nptr == COLON && !_tcsnicmp(nptr+1, TEXT("OFF"), 3)) {
|
||
|
fDelayedExpansion = FALSE;
|
||
|
mystrcpy(nptr, nptr+4) ;
|
||
|
} else {
|
||
|
fDelayedExpansion = TRUE;
|
||
|
if (!_tcsnicmp(nptr, TEXT(":ON"), 3)) {
|
||
|
mystrcpy(nptr, nptr+3) ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the foreground/background screen coler
|
||
|
// enable and /F:OFF to disable.
|
||
|
//
|
||
|
} else if (fEnableExtensions && a == TEXT('t')) { /* Define start color */
|
||
|
if (*(nptr+2) == __TEXT(':') && _istxdigit(*(nptr+3)) &&
|
||
|
_istxdigit(*(nptr+4)) && !_istxdigit(*(nptr+5))) {
|
||
|
wDefaultColor = (WORD) (_istdigit(*(nptr+3)) ? (WORD)*(nptr+3) - (WORD)TEXT('0')
|
||
|
: (WORD)_totlower(*(nptr+3)) - (WORD)TEXT('W')) ;
|
||
|
wDefaultColor <<= 4;
|
||
|
wDefaultColor |= (WORD) (_istdigit(*(nptr+4)) ? (WORD)*(nptr+4) - (WORD)TEXT('0')
|
||
|
: (WORD)_totlower(*(nptr+4)) - (WORD)TEXT('W')) ;
|
||
|
mystrcpy(nptr+2, nptr+5 );
|
||
|
}
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
|
||
|
} else if (a == TEXT('s') ) { /* CMD inserts when parsing pipes */
|
||
|
fAutoGen = TRUE ;
|
||
|
mystrcpy(nptr, nptr+2) ;
|
||
|
} else {
|
||
|
mystrcpy(nptr, nptr+2) ; /* Remove any other switches */
|
||
|
} ;
|
||
|
} ;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** SetUpEnvironment - initialize Command's environment
|
||
|
*
|
||
|
* Purpose:
|
||
|
* Take the environment pointer received earlier and initialize it
|
||
|
* with respect with respect to size and maxsize. Initialize the
|
||
|
* PATH and COMSPEC variables as necessary.
|
||
|
*
|
||
|
* SetUpEnvironment()
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
extern TCHAR KeysStr[]; /* @@5 */
|
||
|
extern int KeysFlag; /* @@5 */
|
||
|
|
||
|
void SetUpEnvironment(void)
|
||
|
{
|
||
|
TCHAR *cds ; // Command directory string
|
||
|
TCHAR *nptr ; // Temp cmd name ptr
|
||
|
TCHAR *eptr ; // Temp cmd name ptr
|
||
|
MEMORY_BASIC_INFORMATION MemInfo;
|
||
|
|
||
|
|
||
|
eptr = CmdEnv.handle;
|
||
|
CmdEnv.cursize = GetEnvCb( eptr );
|
||
|
VirtualQuery( CmdEnv.handle, &MemInfo, sizeof( MemInfo ));
|
||
|
CmdEnv.maxsize = (UINT)MemInfo.RegionSize;
|
||
|
|
||
|
if (!(cds = mkstr(MAX_PATH*sizeof(TCHAR)))) {
|
||
|
PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS);
|
||
|
CMDexit(1) ;
|
||
|
}
|
||
|
GetModuleFileName( NULL, cds, MAX_PATH );
|
||
|
|
||
|
//
|
||
|
// If the PATH variable is not set, it must be added as a NULL. This is
|
||
|
// so that DOS apps inherit the current directory path.
|
||
|
//
|
||
|
if (!GetEnvVar(PathStr)) {
|
||
|
|
||
|
SetEnvVar(PathStr, TEXT(""), &CmdEnv);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the PATHEXT variable is not set, and extensions are enabled, set it to
|
||
|
// the default list of extensions that will be searched.
|
||
|
//
|
||
|
if (!GetEnvVar(PathExtStr)) {
|
||
|
|
||
|
SetEnvVar(PathExtStr, PathExtDefaultStr, &CmdEnv);
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the PROMPT variable is not set, it must be added as $P$G. This is
|
||
|
// special cased, since we do not allow users to add NULLs.
|
||
|
//
|
||
|
if (!GetEnvVar(PromptStr)) {
|
||
|
|
||
|
SetEnvVar(PromptStr, TEXT("$P$G"), &CmdEnv);
|
||
|
}
|
||
|
|
||
|
if (!GetEnvVar(ComSpecStr)) {
|
||
|
|
||
|
DEBUG((INGRP, EILVL, "SETENV: No COMSPEC var")) ;
|
||
|
|
||
|
if(!mystrchr(cds,DOT)) { /* If no fname, use default */
|
||
|
_tcsupr(CmdSpec);
|
||
|
if((cds+mystrlen(cds)-1) != mystrrchr(cds,PathChar)) {
|
||
|
mystrcat(cds,ComSpec) ;
|
||
|
} else {
|
||
|
mystrcat(cds,&ComSpec[1]) ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SetEnvVar(ComSpecStr, cds, &CmdEnv) ;
|
||
|
}
|
||
|
|
||
|
if ( (nptr = GetEnvVar(KeysStr)) && (!_tcsicmp(nptr, TEXT("ON"))) ) {
|
||
|
KeysFlag = 1;
|
||
|
}
|
||
|
|
||
|
ChangeDir(CurDrvDir);
|
||
|
|
||
|
penvOrig = CopyEnv();
|
||
|
if (penvOrig) {
|
||
|
OrigEnv = *penvOrig;
|
||
|
penvOrig = &OrigEnv;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ResetCtrlC() {
|
||
|
|
||
|
EnterCriticalSection(lpcritCtrlC);
|
||
|
CtrlCSeen = FALSE;
|
||
|
LeaveCriticalSection(lpcritCtrlC);
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
SetCtrlC() {
|
||
|
|
||
|
EnterCriticalSection(lpcritCtrlC);
|
||
|
CtrlCSeen = TRUE;
|
||
|
LeaveCriticalSection(lpcritCtrlC);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
CMDexit(int rc)
|
||
|
{
|
||
|
while (ePopDir(NULL) == SUCCESS)
|
||
|
;
|
||
|
|
||
|
exit(rc);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the current OS version and put it into the common version format
|
||
|
//
|
||
|
|
||
|
VOID
|
||
|
GetVersionString(
|
||
|
IN OUT PTCHAR VersionString,
|
||
|
IN ULONG Length
|
||
|
)
|
||
|
{
|
||
|
ULONG vrs = GetVersion();
|
||
|
|
||
|
//
|
||
|
// Version format is [Major.Minor(2).Build(4)]
|
||
|
//
|
||
|
|
||
|
_sntprintf( VersionString, Length,
|
||
|
TEXT( "%d.%d.%04d" ),
|
||
|
vrs & 0xFF,
|
||
|
(vrs >> 8) & 0xFF,
|
||
|
(vrs >> 16) & 0x3FFF
|
||
|
);
|
||
|
}
|
||
|
|