565 lines
14 KiB
C
565 lines
14 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1998 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
dispatch.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module implements the basic command dispatcher.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Wesley Witt (wesw) 21-Oct-1998
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "cmdcons.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
ULONG
|
||
|
RcCmdDoHelp(
|
||
|
IN PTOKENIZED_LINE TokenizedLine
|
||
|
);
|
||
|
|
||
|
ULONG
|
||
|
RcCmdDoExit(
|
||
|
IN PTOKENIZED_LINE TokenizedLine
|
||
|
);
|
||
|
|
||
|
RC_CMD Commands[] = {
|
||
|
{ L"ATTRIB", RcCmdAttrib, 1, 2, 0, TRUE },
|
||
|
{ L"BATCH", RcCmdBatch, 1, 2, 0, TRUE },
|
||
|
#if !defined(_DONT_HAVE_BOOTCFG_TESTERS_)
|
||
|
#if defined(_X86_)
|
||
|
{ L"BOOTCFG", RcCmdBootCfg, 0,-1, 0, TRUE },
|
||
|
#endif
|
||
|
#endif
|
||
|
{ L"CD", RcCmdChdir, 0, 1, 0, TRUE },
|
||
|
{ L"CHDIR", RcCmdChdir, 0, 1, 0, TRUE },
|
||
|
{ L"CHKDSK", RcCmdChkdsk, 0,-1, 0, TRUE },
|
||
|
{ L"CLS", RcCmdCls, 0, 1, 0, TRUE },
|
||
|
{ L"COPY", RcCmdCopy, 1, 2, 0, TRUE },
|
||
|
{ L"DEL", RcCmdDelete, 1, 1, 0, TRUE },
|
||
|
{ L"DELETE", RcCmdDelete, 1, 1, 0, TRUE },
|
||
|
{ L"DIR", RcCmdDir, 0, 1, 0, TRUE },
|
||
|
{ L"DISABLE", RcCmdDisableService, 0,-1, 0, TRUE },
|
||
|
{ L"DISKPART", RcCmdFdisk, 0, 3, 0, TRUE },
|
||
|
{ L"ENABLE", RcCmdEnableService, 0,-1, 0, TRUE },
|
||
|
{ L"ERASE", RcCmdDelete, 1, 1, 1, TRUE },
|
||
|
{ L"EXIT", RcCmdDoExit, 0, 1, 0, TRUE },
|
||
|
{ L"EXPAND", RcCmdExpand, 1,-1, 0, TRUE },
|
||
|
{ L"FIXBOOT", RcCmdFixBootSect, 0, 1, 0, TRUE },
|
||
|
{ L"FIXMBR", RcCmdFixMBR, 0, 1, 0, TRUE },
|
||
|
{ L"FORMAT", RcCmdFormat, 1, 3, 0, TRUE },
|
||
|
{ L"HELP", RcCmdDoHelp, 0, 1, 0, TRUE },
|
||
|
{ L"LISTSVC", RcCmdListSvc, 0, 1, 0, TRUE },
|
||
|
{ L"LOGON", RcCmdLogon, 0, 3, 0, TRUE },
|
||
|
{ L"MAP", RcCmdDriveMap, 0, 1, 0, TRUE },
|
||
|
{ L"MD", RcCmdMkdir, 1, 1, 0, TRUE },
|
||
|
{ L"MKDIR", RcCmdMkdir, 1, 1, 0, TRUE },
|
||
|
{ L"MKDISKRAW", RcCmdMakeDiskRaw, 1, 1, 1, TRUE },
|
||
|
{ L"MORE", RcCmdType, 1, 1, 0, TRUE },
|
||
|
// If you change the command from NET, change it in RcHideNetCommands().
|
||
|
{ L"NET", RcCmdNet, 1, 5, 0, TRUE },
|
||
|
{ L"RD", RcCmdRmdir, 1, 1, 0, TRUE },
|
||
|
{ L"REN", RcCmdRename, 1, 2, 0, TRUE },
|
||
|
{ L"RENAME", RcCmdRename, 1, 2, 0, TRUE },
|
||
|
#if 0
|
||
|
{ L"REPAIR", RcCmdRepair, 1, 5, 0, TRUE },
|
||
|
#endif
|
||
|
{ L"RMDIR", RcCmdRmdir, 1, 1, 0, TRUE },
|
||
|
{ L"SET", RcCmdSetFlags, 0, 3, 1, TRUE },
|
||
|
{ L"SYSTEMROOT", RcCmdSystemRoot, 0, 1, 0, TRUE },
|
||
|
{ L"TYPE", RcCmdType, 1, 1, 0, TRUE },
|
||
|
{ L"VERIFIER", RcCmdVerifier, 0,-1, 1, TRUE },
|
||
|
{ L"/?", RcCmdHelpHelp, 0, 1, 1, TRUE },
|
||
|
{ L"?", RcCmdHelpHelp, 0, 1, 1, TRUE }
|
||
|
};
|
||
|
|
||
|
#define NUM_CMDS (sizeof(Commands)/sizeof(Commands[0]))
|
||
|
|
||
|
//
|
||
|
// Special case: exit and reload
|
||
|
//
|
||
|
#define EXIT_COMMAND_NAME L"EXIT"
|
||
|
#define RELOAD_COMMAND_NAME L"RELOAD"
|
||
|
#define HELP_COMMAND_NAME L"HELP"
|
||
|
|
||
|
// prototype
|
||
|
ULONG
|
||
|
GetStringTokenFromLine(
|
||
|
IN OUT LPWSTR *Start,
|
||
|
OUT LPWSTR Output OPTIONAL
|
||
|
);
|
||
|
|
||
|
PTOKENIZED_LINE
|
||
|
RcTokenizeLine(
|
||
|
IN LPWSTR Line
|
||
|
)
|
||
|
{
|
||
|
ULONG len;
|
||
|
WCHAR *p,*q;
|
||
|
PTOKENIZED_LINE TokenizedLine;
|
||
|
PLINE_TOKEN LineToken,PrevToken;
|
||
|
|
||
|
//
|
||
|
// Strip trailing space off the command.
|
||
|
//
|
||
|
len = wcslen(Line);
|
||
|
while(len && RcIsSpace(Line[len-1])) {
|
||
|
Line[--len] = 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate and initialize a tokenized line structure.
|
||
|
//
|
||
|
TokenizedLine = SpMemAlloc(sizeof(TOKENIZED_LINE));
|
||
|
RtlZeroMemory(TokenizedLine,sizeof(TOKENIZED_LINE));
|
||
|
|
||
|
//
|
||
|
// Now we go into a loop of skipping leading space and parsing in
|
||
|
// the actual tokens.
|
||
|
//
|
||
|
PrevToken = NULL;
|
||
|
p = Line;
|
||
|
while(*p) {
|
||
|
//
|
||
|
// Skip leading space. Because we trimmed off trailing space,
|
||
|
// we should never hit the end of the line before finding a
|
||
|
// non-space character.
|
||
|
//
|
||
|
while(RcIsSpace(*p)) {
|
||
|
p++;
|
||
|
}
|
||
|
ASSERT(*p);
|
||
|
|
||
|
//
|
||
|
// Allocate a line token structure for this string.
|
||
|
//
|
||
|
LineToken = SpMemAlloc(sizeof(LINE_TOKEN));
|
||
|
RtlZeroMemory(LineToken,sizeof(LINE_TOKEN));
|
||
|
|
||
|
//
|
||
|
// Now we've got a string. First we make one pass over it
|
||
|
// to determine the length, then allocate a buffer and
|
||
|
// pull out the string into it.
|
||
|
//
|
||
|
q = p;
|
||
|
len = GetStringTokenFromLine(&q,NULL);
|
||
|
LineToken->String = SpMemAlloc((len+1)*sizeof(WCHAR));
|
||
|
GetStringTokenFromLine(&p,LineToken->String);
|
||
|
|
||
|
if(PrevToken) {
|
||
|
PrevToken->Next = LineToken;
|
||
|
} else {
|
||
|
TokenizedLine->Tokens = LineToken;
|
||
|
}
|
||
|
PrevToken = LineToken;
|
||
|
|
||
|
TokenizedLine->TokenCount++;
|
||
|
}
|
||
|
|
||
|
return(TokenizedLine);
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
GetStringTokenFromLine(
|
||
|
IN OUT LPWSTR *Start,
|
||
|
OUT LPWSTR Output OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
WCHAR *p;
|
||
|
ULONG len;
|
||
|
BOOLEAN InQuote;
|
||
|
|
||
|
len = 0;
|
||
|
InQuote = FALSE;
|
||
|
p = *Start;
|
||
|
|
||
|
while(*p) {
|
||
|
|
||
|
if(RcIsSpace(*p) && !InQuote) {
|
||
|
//
|
||
|
// Done.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if(*p == L'\"') {
|
||
|
InQuote = (BOOLEAN)(!InQuote);
|
||
|
} else {
|
||
|
if(Output) {
|
||
|
Output[len] = *p;
|
||
|
}
|
||
|
len++;
|
||
|
}
|
||
|
|
||
|
p++;
|
||
|
}
|
||
|
|
||
|
if(Output) {
|
||
|
Output[len] = 0;
|
||
|
}
|
||
|
|
||
|
*Start = p;
|
||
|
return(len);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
RcFreeTokenizedLine(
|
||
|
IN OUT PTOKENIZED_LINE *TokenizedLine
|
||
|
)
|
||
|
{
|
||
|
PTOKENIZED_LINE p;
|
||
|
PLINE_TOKEN q,n;
|
||
|
|
||
|
p = *TokenizedLine;
|
||
|
*TokenizedLine = NULL;
|
||
|
|
||
|
q = p->Tokens;
|
||
|
while(q) {
|
||
|
n = q->Next;
|
||
|
SpMemFree((PVOID)q->String);
|
||
|
SpMemFree(q);
|
||
|
q = n;
|
||
|
}
|
||
|
|
||
|
SpMemFree(p);
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
RcDispatchCommand(
|
||
|
IN PTOKENIZED_LINE TokenizedLine
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Top-level routine for dispatching a command.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
unsigned i;
|
||
|
unsigned count;
|
||
|
|
||
|
ASSERT(TokenizedLine->TokenCount);
|
||
|
if(!TokenizedLine->TokenCount) {
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
//
|
||
|
// Special case exit right up front.
|
||
|
//
|
||
|
if(!_wcsicmp(TokenizedLine->Tokens->String,EXIT_COMMAND_NAME)) {
|
||
|
if (TokenizedLine->TokenCount > 1) {
|
||
|
RcMessageOut(MSG_EXIT_HELP);
|
||
|
return(1);
|
||
|
}
|
||
|
return(0);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
if(!_wcsicmp(TokenizedLine->Tokens->String,RELOAD_COMMAND_NAME)) {
|
||
|
return(2);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
if(!_wcsicmp(TokenizedLine->Tokens->String,HELP_COMMAND_NAME)) {
|
||
|
if( RcCmdDoHelp(TokenizedLine) ) {
|
||
|
// if we got a 1, then the user just wanted a help index
|
||
|
// otherwise we want to drop down and let the regular command
|
||
|
// processing path handle a /? parameter.
|
||
|
return(1);
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//
|
||
|
// See whether it's a drive designation.
|
||
|
//
|
||
|
if(RcIsAlpha(TokenizedLine->Tokens->String[0])
|
||
|
&& (TokenizedLine->Tokens->String[1] == L':')
|
||
|
&& (TokenizedLine->Tokens->String[2] == 0)) {
|
||
|
|
||
|
RcCmdSwitchDrives(TokenizedLine->Tokens->String[0]);
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Attempt to locate the command in our table.
|
||
|
//
|
||
|
for(i=0; i<NUM_CMDS; i++) {
|
||
|
|
||
|
if(Commands[i].Enabled && !_wcsicmp(TokenizedLine->Tokens->String,Commands[i].Name)) {
|
||
|
//
|
||
|
// Validate arg count.
|
||
|
//
|
||
|
count = TokenizedLine->TokenCount - 1;
|
||
|
if((count < Commands[i].MinimumArgCount)
|
||
|
|| (count > Commands[i].MaximumArgCount)) {
|
||
|
|
||
|
RcMessageOut(MSG_SYNTAX_ERROR);
|
||
|
} else {
|
||
|
|
||
|
return Commands[i].Routine(TokenizedLine);
|
||
|
}
|
||
|
|
||
|
return(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RcMessageOut(MSG_UNKNOWN_COMMAND);
|
||
|
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
ULONG
|
||
|
RcCmdDoExit(
|
||
|
IN PTOKENIZED_LINE TokenizedLine
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Exit command routine
|
||
|
|
||
|
Arguments:
|
||
|
Tokens for the command
|
||
|
|
||
|
Return Value:
|
||
|
1 if some error was found or help was asked for.
|
||
|
0 if we need to exit
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG uResult = 0; // will exit
|
||
|
|
||
|
if (RcCmdParseHelp( TokenizedLine, MSG_EXIT_HELP ))
|
||
|
uResult = 1; // will not exit
|
||
|
|
||
|
return uResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
RcCmdDoHelp(
|
||
|
IN PTOKENIZED_LINE TokenizedLine
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Help command routine
|
||
|
|
||
|
Arguments:
|
||
|
Tokens for the command
|
||
|
|
||
|
Return Value:
|
||
|
1 if some error was found or help was requested.
|
||
|
When help was reqeusted for a particular command
|
||
|
the dispatched command's return value with "/?" as argument for
|
||
|
the command
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG uResult = 1;
|
||
|
int i;
|
||
|
PLINE_TOKEN Token;
|
||
|
|
||
|
if (!RcCmdParseHelp( TokenizedLine, MSG_HELPCOMMAND_HELP )) {
|
||
|
if (TokenizedLine->TokenCount == 2) {
|
||
|
// we assume that the user is typing HELP <command>.
|
||
|
// we simply reverse the two tokens, getting <command> HELP
|
||
|
// and overwrite HELP with /? [which fits since HELP is four chars long]
|
||
|
|
||
|
// we then return a 0, which causes the dispatcher to drop into
|
||
|
// the normal command processing path
|
||
|
|
||
|
Token = TokenizedLine->Tokens;
|
||
|
TokenizedLine->Tokens = TokenizedLine->Tokens->Next;
|
||
|
TokenizedLine->Tokens->Next = Token;
|
||
|
Token->Next = NULL;
|
||
|
wcscpy( Token->String, L"/?" );
|
||
|
|
||
|
uResult = RcDispatchCommand( TokenizedLine );
|
||
|
} else {
|
||
|
pRcEnableMoreMode();
|
||
|
RcMessageOut( MSG_HELPCOMMAND_HELP );
|
||
|
|
||
|
for( i=0; i < NUM_CMDS; i++ ) {
|
||
|
if (Commands[i].Hidden == 0) {
|
||
|
RcTextOut( Commands[i].Name );
|
||
|
RcTextOut( L"\r\n" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pRcDisableMoreMode();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return uResult;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Enables or disables the SET command
|
||
|
|
||
|
Note : we avoid using direct SET command index
|
||
|
into Commands array so that if some one changes
|
||
|
the Commands array this routine still works
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
bEnable - BOOLEAN inidicating whether to enable or disable
|
||
|
the set command
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
--*/
|
||
|
VOID
|
||
|
RcSetSETCommandStatus(
|
||
|
BOOLEAN bEnabled
|
||
|
)
|
||
|
{
|
||
|
int iIndex;
|
||
|
int cElements = sizeof(Commands) / sizeof(RC_CMD);
|
||
|
WCHAR *szSetCmdName = L"SET";
|
||
|
|
||
|
//
|
||
|
// search through the dispatch table and trun on the
|
||
|
// help flag. This flag will indicate whether the set
|
||
|
// command is enabled or not
|
||
|
//
|
||
|
for(iIndex = 0; iIndex < cElements; iIndex++) {
|
||
|
if ( !wcscmp(Commands[iIndex].Name, szSetCmdName) ) {
|
||
|
Commands[iIndex].Hidden = bEnabled ? 0 : 1;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Returns the SET command status
|
||
|
|
||
|
Note : we avoid using direct SET command index
|
||
|
into Commands array so that if some one changes
|
||
|
the Commands array this routine still works
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
BOOLEAN inidicating whether the SET command is
|
||
|
enabled or disabled.
|
||
|
|
||
|
--*/
|
||
|
BOOLEAN
|
||
|
RcGetSETCommandStatus(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
BOOLEAN bEnabled = FALSE;
|
||
|
int iIndex;
|
||
|
int cElements = sizeof(Commands) / sizeof(RC_CMD);
|
||
|
WCHAR *szSetCmdName = L"SET";
|
||
|
|
||
|
//
|
||
|
// search through the dispatch table and trun on the
|
||
|
// help flag. This flag will indicate whether the set
|
||
|
// command is enabled or not
|
||
|
//
|
||
|
for(iIndex = 0; iIndex < cElements; iIndex++) {
|
||
|
if ( !wcscmp(Commands[iIndex].Name, szSetCmdName) ) {
|
||
|
bEnabled = (Commands[iIndex].Hidden == 0);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bEnabled;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
RcDisableCommand(
|
||
|
IN PRC_CMD_ROUTINE CmdToDisable
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Disables the specified command and hides it.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CmdToDisable - Command Routine to disable
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
BOOLEAN inidicating whether the command was disabled or not.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG Index;
|
||
|
ULONG NumCmds;
|
||
|
BOOLEAN Result = FALSE;
|
||
|
|
||
|
if (CmdToDisable) {
|
||
|
NumCmds = sizeof(Commands) / sizeof(RC_CMD);
|
||
|
|
||
|
for (Index=0; Index < NumCmds; Index++) {
|
||
|
//
|
||
|
// Note : Search the whole table as there might
|
||
|
// be aliases for the same command
|
||
|
//
|
||
|
if (CmdToDisable == Commands[Index].Routine) {
|
||
|
Commands[Index].Hidden = TRUE;
|
||
|
Commands[Index].Enabled = FALSE;
|
||
|
Result = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
RcHideNetCommands(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
ULONG i;
|
||
|
|
||
|
for( i=0; i < NUM_CMDS; i++ ) {
|
||
|
if (wcscmp(Commands[i].Name, L"NET")) {
|
||
|
Commands[i].Hidden = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|