Source code of Windows XP (NT5)
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.

564 lines
14 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. dispatch.c
  5. Abstract:
  6. This module implements the basic command dispatcher.
  7. Author:
  8. Wesley Witt (wesw) 21-Oct-1998
  9. Revision History:
  10. --*/
  11. #include "cmdcons.h"
  12. #pragma hdrstop
  13. ULONG
  14. RcCmdDoHelp(
  15. IN PTOKENIZED_LINE TokenizedLine
  16. );
  17. ULONG
  18. RcCmdDoExit(
  19. IN PTOKENIZED_LINE TokenizedLine
  20. );
  21. RC_CMD Commands[] = {
  22. { L"ATTRIB", RcCmdAttrib, 1, 2, 0, TRUE },
  23. { L"BATCH", RcCmdBatch, 1, 2, 0, TRUE },
  24. #if !defined(_DONT_HAVE_BOOTCFG_TESTERS_)
  25. #if defined(_X86_)
  26. { L"BOOTCFG", RcCmdBootCfg, 0,-1, 0, TRUE },
  27. #endif
  28. #endif
  29. { L"CD", RcCmdChdir, 0, 1, 0, TRUE },
  30. { L"CHDIR", RcCmdChdir, 0, 1, 0, TRUE },
  31. { L"CHKDSK", RcCmdChkdsk, 0,-1, 0, TRUE },
  32. { L"CLS", RcCmdCls, 0, 1, 0, TRUE },
  33. { L"COPY", RcCmdCopy, 1, 2, 0, TRUE },
  34. { L"DEL", RcCmdDelete, 1, 1, 0, TRUE },
  35. { L"DELETE", RcCmdDelete, 1, 1, 0, TRUE },
  36. { L"DIR", RcCmdDir, 0, 1, 0, TRUE },
  37. { L"DISABLE", RcCmdDisableService, 0,-1, 0, TRUE },
  38. { L"DISKPART", RcCmdFdisk, 0, 3, 0, TRUE },
  39. { L"ENABLE", RcCmdEnableService, 0,-1, 0, TRUE },
  40. { L"ERASE", RcCmdDelete, 1, 1, 1, TRUE },
  41. { L"EXIT", RcCmdDoExit, 0, 1, 0, TRUE },
  42. { L"EXPAND", RcCmdExpand, 1,-1, 0, TRUE },
  43. { L"FIXBOOT", RcCmdFixBootSect, 0, 1, 0, TRUE },
  44. { L"FIXMBR", RcCmdFixMBR, 0, 1, 0, TRUE },
  45. { L"FORMAT", RcCmdFormat, 1, 3, 0, TRUE },
  46. { L"HELP", RcCmdDoHelp, 0, 1, 0, TRUE },
  47. { L"LISTSVC", RcCmdListSvc, 0, 1, 0, TRUE },
  48. { L"LOGON", RcCmdLogon, 0, 3, 0, TRUE },
  49. { L"MAP", RcCmdDriveMap, 0, 1, 0, TRUE },
  50. { L"MD", RcCmdMkdir, 1, 1, 0, TRUE },
  51. { L"MKDIR", RcCmdMkdir, 1, 1, 0, TRUE },
  52. { L"MKDISKRAW", RcCmdMakeDiskRaw, 1, 1, 1, TRUE },
  53. { L"MORE", RcCmdType, 1, 1, 0, TRUE },
  54. // If you change the command from NET, change it in RcHideNetCommands().
  55. { L"NET", RcCmdNet, 1, 5, 0, TRUE },
  56. { L"RD", RcCmdRmdir, 1, 1, 0, TRUE },
  57. { L"REN", RcCmdRename, 1, 2, 0, TRUE },
  58. { L"RENAME", RcCmdRename, 1, 2, 0, TRUE },
  59. #if 0
  60. { L"REPAIR", RcCmdRepair, 1, 5, 0, TRUE },
  61. #endif
  62. { L"RMDIR", RcCmdRmdir, 1, 1, 0, TRUE },
  63. { L"SET", RcCmdSetFlags, 0, 3, 1, TRUE },
  64. { L"SYSTEMROOT", RcCmdSystemRoot, 0, 1, 0, TRUE },
  65. { L"TYPE", RcCmdType, 1, 1, 0, TRUE },
  66. { L"VERIFIER", RcCmdVerifier, 0,-1, 1, TRUE },
  67. { L"/?", RcCmdHelpHelp, 0, 1, 1, TRUE },
  68. { L"?", RcCmdHelpHelp, 0, 1, 1, TRUE }
  69. };
  70. #define NUM_CMDS (sizeof(Commands)/sizeof(Commands[0]))
  71. //
  72. // Special case: exit and reload
  73. //
  74. #define EXIT_COMMAND_NAME L"EXIT"
  75. #define RELOAD_COMMAND_NAME L"RELOAD"
  76. #define HELP_COMMAND_NAME L"HELP"
  77. // prototype
  78. ULONG
  79. GetStringTokenFromLine(
  80. IN OUT LPWSTR *Start,
  81. OUT LPWSTR Output OPTIONAL
  82. );
  83. PTOKENIZED_LINE
  84. RcTokenizeLine(
  85. IN LPWSTR Line
  86. )
  87. {
  88. ULONG len;
  89. WCHAR *p,*q;
  90. PTOKENIZED_LINE TokenizedLine;
  91. PLINE_TOKEN LineToken,PrevToken;
  92. //
  93. // Strip trailing space off the command.
  94. //
  95. len = wcslen(Line);
  96. while(len && RcIsSpace(Line[len-1])) {
  97. Line[--len] = 0;
  98. }
  99. //
  100. // Allocate and initialize a tokenized line structure.
  101. //
  102. TokenizedLine = SpMemAlloc(sizeof(TOKENIZED_LINE));
  103. RtlZeroMemory(TokenizedLine,sizeof(TOKENIZED_LINE));
  104. //
  105. // Now we go into a loop of skipping leading space and parsing in
  106. // the actual tokens.
  107. //
  108. PrevToken = NULL;
  109. p = Line;
  110. while(*p) {
  111. //
  112. // Skip leading space. Because we trimmed off trailing space,
  113. // we should never hit the end of the line before finding a
  114. // non-space character.
  115. //
  116. while(RcIsSpace(*p)) {
  117. p++;
  118. }
  119. ASSERT(*p);
  120. //
  121. // Allocate a line token structure for this string.
  122. //
  123. LineToken = SpMemAlloc(sizeof(LINE_TOKEN));
  124. RtlZeroMemory(LineToken,sizeof(LINE_TOKEN));
  125. //
  126. // Now we've got a string. First we make one pass over it
  127. // to determine the length, then allocate a buffer and
  128. // pull out the string into it.
  129. //
  130. q = p;
  131. len = GetStringTokenFromLine(&q,NULL);
  132. LineToken->String = SpMemAlloc((len+1)*sizeof(WCHAR));
  133. GetStringTokenFromLine(&p,LineToken->String);
  134. if(PrevToken) {
  135. PrevToken->Next = LineToken;
  136. } else {
  137. TokenizedLine->Tokens = LineToken;
  138. }
  139. PrevToken = LineToken;
  140. TokenizedLine->TokenCount++;
  141. }
  142. return(TokenizedLine);
  143. }
  144. ULONG
  145. GetStringTokenFromLine(
  146. IN OUT LPWSTR *Start,
  147. OUT LPWSTR Output OPTIONAL
  148. )
  149. {
  150. WCHAR *p;
  151. ULONG len;
  152. BOOLEAN InQuote;
  153. len = 0;
  154. InQuote = FALSE;
  155. p = *Start;
  156. while(*p) {
  157. if(RcIsSpace(*p) && !InQuote) {
  158. //
  159. // Done.
  160. //
  161. break;
  162. }
  163. if(*p == L'\"') {
  164. InQuote = (BOOLEAN)(!InQuote);
  165. } else {
  166. if(Output) {
  167. Output[len] = *p;
  168. }
  169. len++;
  170. }
  171. p++;
  172. }
  173. if(Output) {
  174. Output[len] = 0;
  175. }
  176. *Start = p;
  177. return(len);
  178. }
  179. VOID
  180. RcFreeTokenizedLine(
  181. IN OUT PTOKENIZED_LINE *TokenizedLine
  182. )
  183. {
  184. PTOKENIZED_LINE p;
  185. PLINE_TOKEN q,n;
  186. p = *TokenizedLine;
  187. *TokenizedLine = NULL;
  188. q = p->Tokens;
  189. while(q) {
  190. n = q->Next;
  191. SpMemFree((PVOID)q->String);
  192. SpMemFree(q);
  193. q = n;
  194. }
  195. SpMemFree(p);
  196. }
  197. ULONG
  198. RcDispatchCommand(
  199. IN PTOKENIZED_LINE TokenizedLine
  200. )
  201. /*++
  202. Routine Description:
  203. Top-level routine for dispatching a command.
  204. Arguments:
  205. Return Value:
  206. --*/
  207. {
  208. unsigned i;
  209. unsigned count;
  210. ASSERT(TokenizedLine->TokenCount);
  211. if(!TokenizedLine->TokenCount) {
  212. return(1);
  213. }
  214. /*
  215. //
  216. // Special case exit right up front.
  217. //
  218. if(!_wcsicmp(TokenizedLine->Tokens->String,EXIT_COMMAND_NAME)) {
  219. if (TokenizedLine->TokenCount > 1) {
  220. RcMessageOut(MSG_EXIT_HELP);
  221. return(1);
  222. }
  223. return(0);
  224. }
  225. */
  226. if(!_wcsicmp(TokenizedLine->Tokens->String,RELOAD_COMMAND_NAME)) {
  227. return(2);
  228. }
  229. /*
  230. if(!_wcsicmp(TokenizedLine->Tokens->String,HELP_COMMAND_NAME)) {
  231. if( RcCmdDoHelp(TokenizedLine) ) {
  232. // if we got a 1, then the user just wanted a help index
  233. // otherwise we want to drop down and let the regular command
  234. // processing path handle a /? parameter.
  235. return(1);
  236. }
  237. }
  238. */
  239. //
  240. // See whether it's a drive designation.
  241. //
  242. if(RcIsAlpha(TokenizedLine->Tokens->String[0])
  243. && (TokenizedLine->Tokens->String[1] == L':')
  244. && (TokenizedLine->Tokens->String[2] == 0)) {
  245. RcCmdSwitchDrives(TokenizedLine->Tokens->String[0]);
  246. return(1);
  247. }
  248. //
  249. // Attempt to locate the command in our table.
  250. //
  251. for(i=0; i<NUM_CMDS; i++) {
  252. if(Commands[i].Enabled && !_wcsicmp(TokenizedLine->Tokens->String,Commands[i].Name)) {
  253. //
  254. // Validate arg count.
  255. //
  256. count = TokenizedLine->TokenCount - 1;
  257. if((count < Commands[i].MinimumArgCount)
  258. || (count > Commands[i].MaximumArgCount)) {
  259. RcMessageOut(MSG_SYNTAX_ERROR);
  260. } else {
  261. return Commands[i].Routine(TokenizedLine);
  262. }
  263. return(1);
  264. }
  265. }
  266. RcMessageOut(MSG_UNKNOWN_COMMAND);
  267. return(1);
  268. }
  269. ULONG
  270. RcCmdDoExit(
  271. IN PTOKENIZED_LINE TokenizedLine
  272. )
  273. /*++
  274. Routine Description:
  275. Exit command routine
  276. Arguments:
  277. Tokens for the command
  278. Return Value:
  279. 1 if some error was found or help was asked for.
  280. 0 if we need to exit
  281. --*/
  282. {
  283. ULONG uResult = 0; // will exit
  284. if (RcCmdParseHelp( TokenizedLine, MSG_EXIT_HELP ))
  285. uResult = 1; // will not exit
  286. return uResult;
  287. }
  288. ULONG
  289. RcCmdDoHelp(
  290. IN PTOKENIZED_LINE TokenizedLine
  291. )
  292. /*++
  293. Routine Description:
  294. Help command routine
  295. Arguments:
  296. Tokens for the command
  297. Return Value:
  298. 1 if some error was found or help was requested.
  299. When help was reqeusted for a particular command
  300. the dispatched command's return value with "/?" as argument for
  301. the command
  302. --*/
  303. {
  304. ULONG uResult = 1;
  305. int i;
  306. PLINE_TOKEN Token;
  307. if (!RcCmdParseHelp( TokenizedLine, MSG_HELPCOMMAND_HELP )) {
  308. if (TokenizedLine->TokenCount == 2) {
  309. // we assume that the user is typing HELP <command>.
  310. // we simply reverse the two tokens, getting <command> HELP
  311. // and overwrite HELP with /? [which fits since HELP is four chars long]
  312. // we then return a 0, which causes the dispatcher to drop into
  313. // the normal command processing path
  314. Token = TokenizedLine->Tokens;
  315. TokenizedLine->Tokens = TokenizedLine->Tokens->Next;
  316. TokenizedLine->Tokens->Next = Token;
  317. Token->Next = NULL;
  318. wcscpy( Token->String, L"/?" );
  319. uResult = RcDispatchCommand( TokenizedLine );
  320. } else {
  321. pRcEnableMoreMode();
  322. RcMessageOut( MSG_HELPCOMMAND_HELP );
  323. for( i=0; i < NUM_CMDS; i++ ) {
  324. if (Commands[i].Hidden == 0) {
  325. RcTextOut( Commands[i].Name );
  326. RcTextOut( L"\r\n" );
  327. }
  328. }
  329. pRcDisableMoreMode();
  330. }
  331. }
  332. return uResult;
  333. }
  334. /*++
  335. Routine Description:
  336. Enables or disables the SET command
  337. Note : we avoid using direct SET command index
  338. into Commands array so that if some one changes
  339. the Commands array this routine still works
  340. Arguments:
  341. bEnable - BOOLEAN inidicating whether to enable or disable
  342. the set command
  343. Return Value:
  344. None
  345. --*/
  346. VOID
  347. RcSetSETCommandStatus(
  348. BOOLEAN bEnabled
  349. )
  350. {
  351. int iIndex;
  352. int cElements = sizeof(Commands) / sizeof(RC_CMD);
  353. WCHAR *szSetCmdName = L"SET";
  354. //
  355. // search through the dispatch table and trun on the
  356. // help flag. This flag will indicate whether the set
  357. // command is enabled or not
  358. //
  359. for(iIndex = 0; iIndex < cElements; iIndex++) {
  360. if ( !wcscmp(Commands[iIndex].Name, szSetCmdName) ) {
  361. Commands[iIndex].Hidden = bEnabled ? 0 : 1;
  362. break;
  363. }
  364. }
  365. }
  366. /*++
  367. Routine Description:
  368. Returns the SET command status
  369. Note : we avoid using direct SET command index
  370. into Commands array so that if some one changes
  371. the Commands array this routine still works
  372. Arguments:
  373. None
  374. Return Value:
  375. BOOLEAN inidicating whether the SET command is
  376. enabled or disabled.
  377. --*/
  378. BOOLEAN
  379. RcGetSETCommandStatus(
  380. VOID
  381. )
  382. {
  383. BOOLEAN bEnabled = FALSE;
  384. int iIndex;
  385. int cElements = sizeof(Commands) / sizeof(RC_CMD);
  386. WCHAR *szSetCmdName = L"SET";
  387. //
  388. // search through the dispatch table and trun on the
  389. // help flag. This flag will indicate whether the set
  390. // command is enabled or not
  391. //
  392. for(iIndex = 0; iIndex < cElements; iIndex++) {
  393. if ( !wcscmp(Commands[iIndex].Name, szSetCmdName) ) {
  394. bEnabled = (Commands[iIndex].Hidden == 0);
  395. break;
  396. }
  397. }
  398. return bEnabled;
  399. }
  400. BOOLEAN
  401. RcDisableCommand(
  402. IN PRC_CMD_ROUTINE CmdToDisable
  403. )
  404. /*++
  405. Routine Description:
  406. Disables the specified command and hides it.
  407. Arguments:
  408. CmdToDisable - Command Routine to disable
  409. Return Value:
  410. BOOLEAN inidicating whether the command was disabled or not.
  411. --*/
  412. {
  413. ULONG Index;
  414. ULONG NumCmds;
  415. BOOLEAN Result = FALSE;
  416. if (CmdToDisable) {
  417. NumCmds = sizeof(Commands) / sizeof(RC_CMD);
  418. for (Index=0; Index < NumCmds; Index++) {
  419. //
  420. // Note : Search the whole table as there might
  421. // be aliases for the same command
  422. //
  423. if (CmdToDisable == Commands[Index].Routine) {
  424. Commands[Index].Hidden = TRUE;
  425. Commands[Index].Enabled = FALSE;
  426. Result = TRUE;
  427. }
  428. }
  429. }
  430. return Result;
  431. }
  432. VOID
  433. RcHideNetCommands(
  434. VOID
  435. )
  436. {
  437. ULONG i;
  438. for( i=0; i < NUM_CMDS; i++ ) {
  439. if (wcscmp(Commands[i].Name, L"NET")) {
  440. Commands[i].Hidden = 0;
  441. }
  442. }
  443. }