]> git.proxmox.com Git - mirror_edk2.git/blame - ShellPkg/Application/Shell/Shell.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
CommitLineData
a405b86d 1/** @file\r
2 This is THE shell (application)\r
3\r
d3912eb9 4 Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>\r
c011b6c9 5 (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>\r
764c21f4 6 Copyright 2015-2018 Dell Technologies.<BR>\r
56ba3746 7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
a405b86d 8\r
9**/\r
10\r
11#include "Shell.h"\r
12\r
13//\r
14// Initialize the global structure\r
15//\r
47d20b54 16SHELL_INFO ShellInfoObject = {\r
a405b86d 17 NULL,\r
18 NULL,\r
19 FALSE,\r
20 FALSE,\r
21 {\r
47d20b54
MK
22 {\r
23 {\r
24 0,\r
25 0,\r
26 0,\r
27 0,\r
28 0,\r
29 0,\r
30 0,\r
31 0,\r
32 0,\r
33 0\r
34 }\r
35 },\r
a405b86d 36 0,\r
37 NULL,\r
38 NULL\r
39 },\r
a405b86d 40 {\r
47d20b54
MK
41 { NULL,NULL }, NULL\r
42 },\r
43 {\r
44 {\r
45 { NULL,NULL }, NULL\r
46 },\r
a405b86d 47 0,\r
48 0,\r
49 TRUE\r
50 },\r
51 NULL,\r
52 0,\r
53 NULL,\r
54 NULL,\r
55 NULL,\r
56 NULL,\r
57 NULL,\r
47d20b54
MK
58 {\r
59 { NULL,NULL }, NULL, NULL\r
60 },\r
61 {\r
62 { NULL,NULL }, NULL, NULL\r
63 },\r
ce68d3bc
SZ
64 NULL,\r
65 NULL,\r
66 NULL,\r
67 NULL,\r
8be0ba36 68 NULL,\r
69 NULL,\r
70 NULL,\r
a49f6a2f 71 NULL,\r
72 FALSE\r
a405b86d 73};\r
74\r
47d20b54
MK
75STATIC CONST CHAR16 mScriptExtension[] = L".NSH";\r
76STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";\r
77STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh";\r
78CONST CHAR16 mNoNestingEnvVarName[] = L"nonesting";\r
79CONST CHAR16 mNoNestingTrue[] = L"True";\r
80CONST CHAR16 mNoNestingFalse[] = L"False";\r
a405b86d 81\r
ad2bc854 82/**\r
51429264 83 Cleans off leading and trailing spaces and tabs.\r
ad2bc854 84\r
51429264 85 @param[in] String pointer to the string to trim them off.\r
ad2bc854
JC
86**/\r
87EFI_STATUS\r
47d20b54
MK
88TrimSpaces (\r
89 IN CHAR16 **String\r
ad2bc854
JC
90 )\r
91{\r
47d20b54
MK
92 ASSERT (String != NULL);\r
93 ASSERT (*String != NULL);\r
ad2bc854
JC
94 //\r
95 // Remove any spaces and tabs at the beginning of the (*String).\r
96 //\r
97 while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {\r
47d20b54 98 CopyMem ((*String), (*String)+1, StrSize ((*String)) - sizeof ((*String)[0]));\r
ad2bc854
JC
99 }\r
100\r
101 //\r
404b3f43 102 // Remove any spaces and tabs at the end of the (*String).\r
ad2bc854 103 //\r
47d20b54
MK
104 while ((StrLen (*String) > 0) && (((*String)[StrLen ((*String))-1] == L' ') || ((*String)[StrLen ((*String))-1] == L'\t'))) {\r
105 (*String)[StrLen ((*String))-1] = CHAR_NULL;\r
ad2bc854
JC
106 }\r
107\r
108 return (EFI_SUCCESS);\r
109}\r
110\r
a95cf8f0 111/**\r
ba0014b9 112 Parse for the next instance of one string within another string. Can optionally make sure that\r
a95cf8f0
JC
113 the string was not escaped (^ character) per the shell specification.\r
114\r
115 @param[in] SourceString The string to search within\r
116 @param[in] FindString The string to look for\r
117 @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances\r
118**/\r
47d20b54
MK
119CHAR16 *\r
120FindNextInstance (\r
a95cf8f0
JC
121 IN CONST CHAR16 *SourceString,\r
122 IN CONST CHAR16 *FindString,\r
123 IN CONST BOOLEAN CheckForEscapeCharacter\r
124 )\r
125{\r
47d20b54
MK
126 CHAR16 *Temp;\r
127\r
a95cf8f0
JC
128 if (SourceString == NULL) {\r
129 return (NULL);\r
130 }\r
47d20b54
MK
131\r
132 Temp = StrStr (SourceString, FindString);\r
a95cf8f0
JC
133\r
134 //\r
d08a5464 135 // If nothing found, or we don't care about escape characters\r
a95cf8f0 136 //\r
47d20b54 137 if ((Temp == NULL) || !CheckForEscapeCharacter) {\r
a95cf8f0
JC
138 return (Temp);\r
139 }\r
140\r
141 //\r
142 // If we found an escaped character, try again on the remainder of the string\r
143 //\r
47d20b54
MK
144 if ((Temp > (SourceString)) && (*(Temp-1) == L'^')) {\r
145 return FindNextInstance (Temp+1, FindString, CheckForEscapeCharacter);\r
a95cf8f0
JC
146 }\r
147\r
148 //\r
149 // we found the right character\r
150 //\r
151 return (Temp);\r
152}\r
153\r
42435671 154/**\r
d08a5464 155 Check whether the string between a pair of % is a valid environment variable name.\r
42435671
QS
156\r
157 @param[in] BeginPercent pointer to the first percent.\r
158 @param[in] EndPercent pointer to the last percent.\r
159\r
160 @retval TRUE is a valid environment variable name.\r
161 @retval FALSE is NOT a valid environment variable name.\r
162**/\r
163BOOLEAN\r
47d20b54
MK
164IsValidEnvironmentVariableName (\r
165 IN CONST CHAR16 *BeginPercent,\r
166 IN CONST CHAR16 *EndPercent\r
42435671
QS
167 )\r
168{\r
47d20b54 169 CONST CHAR16 *Walker;\r
ba0014b9 170\r
42435671
QS
171 Walker = NULL;\r
172\r
173 ASSERT (BeginPercent != NULL);\r
174 ASSERT (EndPercent != NULL);\r
175 ASSERT (BeginPercent < EndPercent);\r
ba0014b9 176\r
42435671
QS
177 if ((BeginPercent + 1) == EndPercent) {\r
178 return FALSE;\r
179 }\r
180\r
181 for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) {\r
182 if (\r
47d20b54
MK
183 ((*Walker >= L'0') && (*Walker <= L'9')) ||\r
184 ((*Walker >= L'A') && (*Walker <= L'Z')) ||\r
185 ((*Walker >= L'a') && (*Walker <= L'z')) ||\r
42435671 186 (*Walker == L'_')\r
47d20b54
MK
187 )\r
188 {\r
189 if ((Walker == BeginPercent + 1) && ((*Walker >= L'0') && (*Walker <= L'9'))) {\r
42435671
QS
190 return FALSE;\r
191 } else {\r
192 continue;\r
193 }\r
194 } else {\r
195 return FALSE;\r
196 }\r
197 }\r
198\r
199 return TRUE;\r
200}\r
201\r
518c8cdc
JC
202/**\r
203 Determine if a command line contains a split operation\r
204\r
205 @param[in] CmdLine The command line to parse.\r
206\r
207 @retval TRUE CmdLine has a valid split.\r
208 @retval FALSE CmdLine does not have a valid split.\r
209**/\r
210BOOLEAN\r
47d20b54
MK
211ContainsSplit (\r
212 IN CONST CHAR16 *CmdLine\r
518c8cdc
JC
213 )\r
214{\r
47d20b54
MK
215 CONST CHAR16 *TempSpot;\r
216 CONST CHAR16 *FirstQuote;\r
217 CONST CHAR16 *SecondQuote;\r
218\r
219 FirstQuote = FindNextInstance (CmdLine, L"\"", TRUE);\r
220 SecondQuote = NULL;\r
221 TempSpot = FindFirstCharacter (CmdLine, L"|", L'^');\r
222\r
223 if ((FirstQuote == NULL) ||\r
224 (TempSpot == NULL) ||\r
225 (TempSpot == CHAR_NULL) ||\r
226 (FirstQuote > TempSpot)\r
227 )\r
228 {\r
229 return (BOOLEAN)((TempSpot != NULL) && (*TempSpot != CHAR_NULL));\r
6f6792b8
QS
230 }\r
231\r
232 while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) {\r
47d20b54 233 if ((FirstQuote == NULL) || (FirstQuote > TempSpot)) {\r
6f6792b8 234 break;\r
ba0014b9 235 }\r
47d20b54 236\r
a95cf8f0 237 SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE);\r
fe8ec3dd 238 if (SecondQuote == NULL) {\r
6f6792b8
QS
239 break;\r
240 }\r
47d20b54 241\r
6f6792b8 242 if (SecondQuote < TempSpot) {\r
a95cf8f0 243 FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);\r
6f6792b8
QS
244 continue;\r
245 } else {\r
a95cf8f0 246 FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);\r
47d20b54 247 TempSpot = FindFirstCharacter (TempSpot + 1, L"|", L'^');\r
6f6792b8 248 continue;\r
ba0014b9 249 }\r
6f6792b8 250 }\r
ba0014b9 251\r
47d20b54 252 return (BOOLEAN)((TempSpot != NULL) && (*TempSpot != CHAR_NULL));\r
19a44972
JC
253}\r
254\r
733f138d 255/**\r
ba0014b9 256 Function to start monitoring for CTRL-S using SimpleTextInputEx. This\r
733f138d 257 feature's enabled state was not known when the shell initially launched.\r
258\r
259 @retval EFI_SUCCESS The feature is enabled.\r
d08a5464 260 @retval EFI_OUT_OF_RESOURCES There is not enough memory available.\r
733f138d 261**/\r
262EFI_STATUS\r
47d20b54 263InternalEfiShellStartCtrlSMonitor (\r
733f138d 264 VOID\r
265 )\r
266{\r
47d20b54
MK
267 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;\r
268 EFI_KEY_DATA KeyData;\r
269 EFI_STATUS Status;\r
270\r
271 Status = gBS->OpenProtocol (\r
272 gST->ConsoleInHandle,\r
273 &gEfiSimpleTextInputExProtocolGuid,\r
274 (VOID **)&SimpleEx,\r
275 gImageHandle,\r
276 NULL,\r
277 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
278 );\r
279 if (EFI_ERROR (Status)) {\r
280 ShellPrintHiiEx (\r
ba0014b9
LG
281 -1,\r
282 -1,\r
733f138d 283 NULL,\r
284 STRING_TOKEN (STR_SHELL_NO_IN_EX),\r
47d20b54
MK
285 ShellInfoObject.HiiHandle\r
286 );\r
733f138d 287 return (EFI_SUCCESS);\r
288 }\r
289\r
290 KeyData.KeyState.KeyToggleState = 0;\r
291 KeyData.Key.ScanCode = 0;\r
292 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
293 KeyData.Key.UnicodeChar = L's';\r
294\r
47d20b54
MK
295 Status = SimpleEx->RegisterKeyNotify (\r
296 SimpleEx,\r
297 &KeyData,\r
298 NotificationFunction,\r
299 &ShellInfoObject.CtrlSNotifyHandle1\r
300 );\r
ba0014b9 301\r
47d20b54
MK
302 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
303 if (!EFI_ERROR (Status)) {\r
304 Status = SimpleEx->RegisterKeyNotify (\r
305 SimpleEx,\r
306 &KeyData,\r
307 NotificationFunction,\r
308 &ShellInfoObject.CtrlSNotifyHandle2\r
309 );\r
733f138d 310 }\r
47d20b54
MK
311\r
312 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
313 KeyData.Key.UnicodeChar = 19;\r
314\r
315 if (!EFI_ERROR (Status)) {\r
316 Status = SimpleEx->RegisterKeyNotify (\r
317 SimpleEx,\r
318 &KeyData,\r
319 NotificationFunction,\r
320 &ShellInfoObject.CtrlSNotifyHandle3\r
321 );\r
733f138d 322 }\r
733f138d 323\r
47d20b54
MK
324 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
325 if (!EFI_ERROR (Status)) {\r
326 Status = SimpleEx->RegisterKeyNotify (\r
327 SimpleEx,\r
328 &KeyData,\r
329 NotificationFunction,\r
330 &ShellInfoObject.CtrlSNotifyHandle4\r
331 );\r
332 }\r
733f138d 333\r
47d20b54
MK
334 return (Status);\r
335}\r
733f138d 336\r
a405b86d 337/**\r
338 The entry point for the application.\r
339\r
340 @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
341 @param[in] SystemTable A pointer to the EFI System Table.\r
342\r
343 @retval EFI_SUCCESS The entry point is executed successfully.\r
344 @retval other Some error occurs when executing this entry point.\r
345\r
346**/\r
347EFI_STATUS\r
348EFIAPI\r
349UefiMain (\r
350 IN EFI_HANDLE ImageHandle,\r
351 IN EFI_SYSTEM_TABLE *SystemTable\r
352 )\r
353{\r
a49f6a2f 354 EFI_STATUS Status;\r
355 CHAR16 *TempString;\r
356 UINTN Size;\r
357 EFI_HANDLE ConInHandle;\r
358 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;\r
bd3fc813 359 SPLIT_LIST *Split;\r
a405b86d 360\r
47d20b54 361 if (PcdGet8 (PcdShellSupportLevel) > 3) {\r
a405b86d 362 return (EFI_UNSUPPORTED);\r
363 }\r
364\r
365 //\r
366 // Clear the screen\r
367 //\r
47d20b54
MK
368 Status = gST->ConOut->ClearScreen (gST->ConOut);\r
369 if (EFI_ERROR (Status)) {\r
3c865f20 370 return (Status);\r
371 }\r
a405b86d 372\r
373 //\r
374 // Populate the global structure from PCDs\r
375 //\r
47d20b54
MK
376 ShellInfoObject.ImageDevPath = NULL;\r
377 ShellInfoObject.FileDevPath = NULL;\r
378 ShellInfoObject.PageBreakEnabled = PcdGetBool (PcdShellPageBreakDefault);\r
379 ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool (PcdShellInsertModeDefault);\r
380 ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount);\r
a405b86d 381\r
382 //\r
383 // verify we dont allow for spec violation\r
384 //\r
47d20b54 385 ASSERT (ShellInfoObject.LogScreenCount >= 3);\r
a405b86d 386\r
387 //\r
388 // Initialize the LIST ENTRY objects...\r
389 //\r
47d20b54
MK
390 InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);\r
391 InitializeListHead (&ShellInfoObject.ViewingSettings.CommandHistory.Link);\r
392 InitializeListHead (&ShellInfoObject.SplitList.Link);\r
a405b86d 393\r
394 //\r
395 // Check PCDs for optional features that are not implemented yet.\r
396 //\r
47d20b54
MK
397 if ( PcdGetBool (PcdShellSupportOldProtocols)\r
398 || !FeaturePcdGet (PcdShellRequireHiiPlatform)\r
399 || FeaturePcdGet (PcdShellSupportFrameworkHii)\r
400 )\r
401 {\r
a405b86d 402 return (EFI_UNSUPPORTED);\r
403 }\r
404\r
405 //\r
406 // turn off the watchdog timer\r
407 //\r
408 gBS->SetWatchdogTimer (0, 0, 0, NULL);\r
409\r
410 //\r
411 // install our console logger. This will keep a log of the output for back-browsing\r
412 //\r
47d20b54
MK
413 Status = ConsoleLoggerInstall (ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);\r
414 if (!EFI_ERROR (Status)) {\r
3c865f20 415 //\r
416 // Enable the cursor to be visible\r
417 //\r
418 gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
a405b86d 419\r
3c865f20 420 //\r
421 // If supporting EFI 1.1 we need to install HII protocol\r
422 // only do this if PcdShellRequireHiiPlatform == FALSE\r
423 //\r
424 // remove EFI_UNSUPPORTED check above when complete.\r
47d20b54 425 /// @todo add support for Framework HII\r
a405b86d 426\r
3c865f20 427 //\r
428 // install our (solitary) HII package\r
429 //\r
430 ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);\r
a405b86d 431 if (ShellInfoObject.HiiHandle == NULL) {\r
47d20b54
MK
432 if (PcdGetBool (PcdShellSupportFrameworkHii)) {\r
433 /// @todo Add our package into Framework HII\r
3c865f20 434 }\r
47d20b54 435\r
3c865f20 436 if (ShellInfoObject.HiiHandle == NULL) {\r
0477054b
BJ
437 Status = EFI_NOT_STARTED;\r
438 goto FreeResources;\r
3c865f20 439 }\r
a405b86d 440 }\r
a405b86d 441\r
3c865f20 442 //\r
443 // create and install the EfiShellParametersProtocol\r
444 //\r
47d20b54
MK
445 Status = CreatePopulateInstallShellParametersProtocol (&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);\r
446 ASSERT_EFI_ERROR (Status);\r
447 ASSERT (ShellInfoObject.NewShellParametersProtocol != NULL);\r
a405b86d 448\r
3c865f20 449 //\r
450 // create and install the EfiShellProtocol\r
451 //\r
47d20b54
MK
452 Status = CreatePopulateInstallShellProtocol (&ShellInfoObject.NewEfiShellProtocol);\r
453 ASSERT_EFI_ERROR (Status);\r
454 ASSERT (ShellInfoObject.NewEfiShellProtocol != NULL);\r
a405b86d 455\r
3c865f20 456 //\r
457 // Now initialize the shell library (it requires Shell Parameters protocol)\r
458 //\r
47d20b54
MK
459 Status = ShellInitialize ();\r
460 ASSERT_EFI_ERROR (Status);\r
a405b86d 461\r
47d20b54
MK
462 Status = CommandInit ();\r
463 ASSERT_EFI_ERROR (Status);\r
a405b86d 464\r
b62bb885
QS
465 Status = ShellInitEnvVarList ();\r
466\r
3c865f20 467 //\r
468 // Check the command line\r
469 //\r
da92bf85
BJ
470 Status = ProcessCommandLine ();\r
471 if (EFI_ERROR (Status)) {\r
472 goto FreeResources;\r
473 }\r
a405b86d 474\r
3c865f20 475 //\r
476 // If shell support level is >= 1 create the mappings and paths\r
477 //\r
47d20b54
MK
478 if (PcdGet8 (PcdShellSupportLevel) >= 1) {\r
479 Status = ShellCommandCreateInitialMappingsAndPaths ();\r
3c865f20 480 }\r
a405b86d 481\r
dcbdb8bf
QS
482 //\r
483 // Set the environment variable for nesting support\r
484 //\r
47d20b54 485 Size = 0;\r
dcbdb8bf
QS
486 TempString = NULL;\r
487 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {\r
488 //\r
489 // No change. require nesting in Shell Protocol Execute()\r
490 //\r
47d20b54
MK
491 StrnCatGrow (\r
492 &TempString,\r
493 &Size,\r
494 L"False",\r
495 0\r
496 );\r
dcbdb8bf 497 } else {\r
47d20b54
MK
498 StrnCatGrow (\r
499 &TempString,\r
500 &Size,\r
501 mNoNestingTrue,\r
502 0\r
503 );\r
dcbdb8bf 504 }\r
47d20b54
MK
505\r
506 Status = InternalEfiShellSetEnv (mNoNestingEnvVarName, TempString, TRUE);\r
507 SHELL_FREE_NON_NULL (TempString);\r
dcbdb8bf
QS
508 Size = 0;\r
509\r
3c865f20 510 //\r
511 // save the device path for the loaded image and the device path for the filepath (under loaded image)\r
512 // These are where to look for the startup.nsh file\r
513 //\r
47d20b54
MK
514 Status = GetDevicePathsForImageAndFile (&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);\r
515 ASSERT_EFI_ERROR (Status);\r
a405b86d 516\r
3c865f20 517 //\r
518 // Display the version\r
519 //\r
520 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {\r
521 ShellPrintHiiEx (\r
522 0,\r
523 gST->ConOut->Mode->CursorRow,\r
524 NULL,\r
284e034f 525 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),\r
3c865f20 526 ShellInfoObject.HiiHandle,\r
47d20b54 527 SupportLevel[PcdGet8 (PcdShellSupportLevel)],\r
3c865f20 528 gEfiShellProtocol->MajorVersion,\r
284e034f 529 gEfiShellProtocol->MinorVersion\r
47d20b54 530 );\r
284e034f
CP
531\r
532 ShellPrintHiiEx (\r
533 -1,\r
534 -1,\r
535 NULL,\r
536 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),\r
537 ShellInfoObject.HiiHandle,\r
47d20b54
MK
538 (CHAR16 *)PcdGetPtr (PcdShellSupplier)\r
539 );\r
284e034f
CP
540\r
541 ShellPrintHiiEx (\r
542 -1,\r
543 -1,\r
544 NULL,\r
545 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),\r
546 ShellInfoObject.HiiHandle,\r
3c865f20 547 (gST->Hdr.Revision&0xffff0000)>>16,\r
548 (gST->Hdr.Revision&0x0000ffff),\r
549 gST->FirmwareVendor,\r
550 gST->FirmwareRevision\r
47d20b54 551 );\r
3c865f20 552 }\r
a405b86d 553\r
3c865f20 554 //\r
555 // Display the mapping\r
556 //\r
47d20b54
MK
557 if ((PcdGet8 (PcdShellSupportLevel) >= 2) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {\r
558 Status = RunCommand (L"map");\r
559 ASSERT_EFI_ERROR (Status);\r
3c865f20 560 }\r
a405b86d 561\r
3c865f20 562 //\r
563 // init all the built in alias'\r
564 //\r
47d20b54
MK
565 Status = SetBuiltInAlias ();\r
566 ASSERT_EFI_ERROR (Status);\r
a405b86d 567\r
3c865f20 568 //\r
569 // Initialize environment variables\r
570 //\r
47d20b54
MK
571 if (ShellCommandGetProfileList () != NULL) {\r
572 Status = InternalEfiShellSetEnv (L"profiles", ShellCommandGetProfileList (), TRUE);\r
573 ASSERT_EFI_ERROR (Status);\r
3c865f20 574 }\r
a405b86d 575\r
47d20b54
MK
576 Size = 100;\r
577 TempString = AllocateZeroPool (Size);\r
a405b86d 578\r
47d20b54
MK
579 UnicodeSPrint (TempString, Size, L"%d", PcdGet8 (PcdShellSupportLevel));\r
580 Status = InternalEfiShellSetEnv (L"uefishellsupport", TempString, TRUE);\r
581 ASSERT_EFI_ERROR (Status);\r
a405b86d 582\r
47d20b54
MK
583 UnicodeSPrint (TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);\r
584 Status = InternalEfiShellSetEnv (L"uefishellversion", TempString, TRUE);\r
585 ASSERT_EFI_ERROR (Status);\r
a405b86d 586\r
47d20b54
MK
587 UnicodeSPrint (TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);\r
588 Status = InternalEfiShellSetEnv (L"uefiversion", TempString, TRUE);\r
589 ASSERT_EFI_ERROR (Status);\r
a405b86d 590\r
47d20b54 591 FreePool (TempString);\r
a405b86d 592\r
47d20b54 593 if (!EFI_ERROR (Status)) {\r
3c865f20 594 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
a405b86d 595 //\r
3c865f20 596 // Set up the event for CTRL-C monitoring...\r
a405b86d 597 //\r
47d20b54 598 Status = InernalEfiShellStartMonitor ();\r
3c865f20 599 }\r
600\r
47d20b54 601 if (!EFI_ERROR (Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
733f138d 602 //\r
603 // Set up the event for CTRL-S monitoring...\r
604 //\r
47d20b54 605 Status = InternalEfiShellStartCtrlSMonitor ();\r
733f138d 606 }\r
607\r
47d20b54 608 if (!EFI_ERROR (Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
a49f6a2f 609 //\r
610 // close off the gST->ConIn\r
611 //\r
47d20b54
MK
612 OldConIn = gST->ConIn;\r
613 ConInHandle = gST->ConsoleInHandle;\r
614 gST->ConIn = CreateSimpleTextInOnFile ((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);\r
a49f6a2f 615 } else {\r
47d20b54
MK
616 OldConIn = NULL;\r
617 ConInHandle = NULL;\r
a49f6a2f 618 }\r
619\r
47d20b54 620 if (!EFI_ERROR (Status) && (PcdGet8 (PcdShellSupportLevel) >= 1)) {\r
a405b86d 621 //\r
3c865f20 622 // process the startup script or launch the called app.\r
a405b86d 623 //\r
47d20b54 624 Status = DoStartupScript (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
3c865f20 625 }\r
a405b86d 626\r
47d20b54 627 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit () && ((PcdGet8 (PcdShellSupportLevel) >= 3) || PcdGetBool (PcdShellForceConsole)) && !EFI_ERROR (Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
a405b86d 628 //\r
3c865f20 629 // begin the UI waiting loop\r
a405b86d 630 //\r
3c865f20 631 do {\r
632 //\r
633 // clean out all the memory allocated for CONST <something> * return values\r
634 // between each shell prompt presentation\r
635 //\r
47d20b54
MK
636 if (!IsListEmpty (&ShellInfoObject.BufferToFreeList.Link)) {\r
637 FreeBufferList (&ShellInfoObject.BufferToFreeList);\r
3c865f20 638 }\r
639\r
640 //\r
641 // Reset page break back to default.\r
642 //\r
47d20b54 643 ShellInfoObject.PageBreakEnabled = PcdGetBool (PcdShellPageBreakDefault);\r
81cd2f53 644 ASSERT (ShellInfoObject.ConsoleInfo != NULL);\r
3c865f20 645 ShellInfoObject.ConsoleInfo->Enabled = TRUE;\r
646 ShellInfoObject.ConsoleInfo->RowCounter = 0;\r
647\r
648 //\r
649 // Display Prompt\r
650 //\r
47d20b54
MK
651 Status = DoShellPrompt ();\r
652 } while (!ShellCommandGetExit ());\r
3c865f20 653 }\r
47d20b54
MK
654\r
655 if ((OldConIn != NULL) && (ConInHandle != NULL)) {\r
a49f6a2f 656 CloseSimpleTextInOnFile (gST->ConIn);\r
47d20b54
MK
657 gST->ConIn = OldConIn;\r
658 gST->ConsoleInHandle = ConInHandle;\r
a49f6a2f 659 }\r
a405b86d 660 }\r
661 }\r
3c865f20 662\r
0477054b 663FreeResources:\r
a405b86d 664 //\r
665 // uninstall protocols / free memory / etc...\r
666 //\r
667 if (ShellInfoObject.UserBreakTimer != NULL) {\r
47d20b54
MK
668 gBS->CloseEvent (ShellInfoObject.UserBreakTimer);\r
669 DEBUG_CODE (\r
670 ShellInfoObject.UserBreakTimer = NULL;\r
671 );\r
a405b86d 672 }\r
47d20b54 673\r
a405b86d 674 if (ShellInfoObject.ImageDevPath != NULL) {\r
47d20b54
MK
675 FreePool (ShellInfoObject.ImageDevPath);\r
676 DEBUG_CODE (\r
677 ShellInfoObject.ImageDevPath = NULL;\r
678 );\r
a405b86d 679 }\r
47d20b54 680\r
a405b86d 681 if (ShellInfoObject.FileDevPath != NULL) {\r
47d20b54
MK
682 FreePool (ShellInfoObject.FileDevPath);\r
683 DEBUG_CODE (\r
684 ShellInfoObject.FileDevPath = NULL;\r
685 );\r
a405b86d 686 }\r
47d20b54 687\r
a405b86d 688 if (ShellInfoObject.NewShellParametersProtocol != NULL) {\r
47d20b54
MK
689 CleanUpShellParametersProtocol (ShellInfoObject.NewShellParametersProtocol);\r
690 DEBUG_CODE (\r
691 ShellInfoObject.NewShellParametersProtocol = NULL;\r
692 );\r
a405b86d 693 }\r
47d20b54
MK
694\r
695 if (ShellInfoObject.NewEfiShellProtocol != NULL) {\r
696 if (ShellInfoObject.NewEfiShellProtocol->IsRootShell ()) {\r
697 InternalEfiShellSetEnv (L"cwd", NULL, TRUE);\r
8be0ba36 698 }\r
47d20b54 699\r
03140346 700 CleanUpShellEnvironment (ShellInfoObject.NewEfiShellProtocol);\r
47d20b54
MK
701 DEBUG_CODE (\r
702 ShellInfoObject.NewEfiShellProtocol = NULL;\r
703 );\r
a405b86d 704 }\r
705\r
47d20b54
MK
706 if (!IsListEmpty (&ShellInfoObject.BufferToFreeList.Link)) {\r
707 FreeBufferList (&ShellInfoObject.BufferToFreeList);\r
a405b86d 708 }\r
709\r
47d20b54
MK
710 if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) {\r
711 ASSERT (FALSE); /// @todo finish this de-allocation (free SplitStdIn/Out when needed).\r
bd3fc813 712\r
47d20b54
MK
713 for ( Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link)\r
714 ; !IsNull (&ShellInfoObject.SplitList.Link, &Split->Link)\r
715 ; Split = (SPLIT_LIST *)GetNextNode (&ShellInfoObject.SplitList.Link, &Split->Link)\r
716 )\r
717 {\r
bd3fc813
MH
718 RemoveEntryList (&Split->Link);\r
719 FreePool (Split);\r
720 }\r
721\r
47d20b54
MK
722 DEBUG_CODE (\r
723 InitializeListHead (&ShellInfoObject.SplitList.Link);\r
724 );\r
a405b86d 725 }\r
726\r
727 if (ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
47d20b54
MK
728 FreePool (ShellInfoObject.ShellInitSettings.FileName);\r
729 DEBUG_CODE (\r
730 ShellInfoObject.ShellInitSettings.FileName = NULL;\r
731 );\r
a405b86d 732 }\r
733\r
734 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
47d20b54
MK
735 FreePool (ShellInfoObject.ShellInitSettings.FileOptions);\r
736 DEBUG_CODE (\r
737 ShellInfoObject.ShellInitSettings.FileOptions = NULL;\r
738 );\r
a405b86d 739 }\r
740\r
741 if (ShellInfoObject.HiiHandle != NULL) {\r
47d20b54
MK
742 HiiRemovePackages (ShellInfoObject.HiiHandle);\r
743 DEBUG_CODE (\r
744 ShellInfoObject.HiiHandle = NULL;\r
745 );\r
a405b86d 746 }\r
747\r
47d20b54
MK
748 if (!IsListEmpty (&ShellInfoObject.ViewingSettings.CommandHistory.Link)) {\r
749 FreeBufferList (&ShellInfoObject.ViewingSettings.CommandHistory);\r
a405b86d 750 }\r
751\r
47d20b54 752 ASSERT (ShellInfoObject.ConsoleInfo != NULL);\r
a405b86d 753 if (ShellInfoObject.ConsoleInfo != NULL) {\r
47d20b54
MK
754 ConsoleLoggerUninstall (ShellInfoObject.ConsoleInfo);\r
755 FreePool (ShellInfoObject.ConsoleInfo);\r
756 DEBUG_CODE (\r
757 ShellInfoObject.ConsoleInfo = NULL;\r
758 );\r
a405b86d 759 }\r
760\r
b62bb885
QS
761 ShellFreeEnvVarList ();\r
762\r
47d20b54
MK
763 if (ShellCommandGetExit ()) {\r
764 return ((EFI_STATUS)ShellCommandGetExitCode ());\r
b6b22b13 765 }\r
47d20b54 766\r
a308e058 767 return (Status);\r
a405b86d 768}\r
769\r
770/**\r
771 Sets all the alias' that were registered with the ShellCommandLib library.\r
772\r
d08a5464 773 @retval EFI_SUCCESS all init commands were run successfully.\r
a405b86d 774**/\r
775EFI_STATUS\r
47d20b54 776SetBuiltInAlias (\r
e7275d3f 777 VOID\r
a405b86d 778 )\r
779{\r
47d20b54
MK
780 EFI_STATUS Status;\r
781 CONST ALIAS_LIST *List;\r
782 ALIAS_LIST *Node;\r
a405b86d 783\r
784 //\r
785 // Get all the commands we want to run\r
786 //\r
47d20b54 787 List = ShellCommandGetInitAliasList ();\r
a405b86d 788\r
789 //\r
790 // for each command in the List\r
791 //\r
47d20b54
MK
792 for ( Node = (ALIAS_LIST *)GetFirstNode (&List->Link)\r
793 ; !IsNull (&List->Link, &Node->Link)\r
794 ; Node = (ALIAS_LIST *)GetNextNode (&List->Link, &Node->Link)\r
795 )\r
796 {\r
a405b86d 797 //\r
798 // install the alias'\r
799 //\r
47d20b54
MK
800 Status = InternalSetAlias (Node->CommandString, Node->Alias, TRUE);\r
801 ASSERT_EFI_ERROR (Status);\r
a405b86d 802 }\r
47d20b54 803\r
a405b86d 804 return (EFI_SUCCESS);\r
805}\r
806\r
807/**\r
808 Internal function to determine if 2 command names are really the same.\r
809\r
810 @param[in] Command1 The pointer to the first command name.\r
811 @param[in] Command2 The pointer to the second command name.\r
812\r
813 @retval TRUE The 2 command names are the same.\r
814 @retval FALSE The 2 command names are not the same.\r
815**/\r
816BOOLEAN\r
47d20b54
MK
817IsCommand (\r
818 IN CONST CHAR16 *Command1,\r
819 IN CONST CHAR16 *Command2\r
a405b86d 820 )\r
821{\r
47d20b54 822 if (StringNoCaseCompare (&Command1, &Command2) == 0) {\r
a405b86d 823 return (TRUE);\r
824 }\r
47d20b54 825\r
a405b86d 826 return (FALSE);\r
827}\r
828\r
829/**\r
830 Internal function to determine if a command is a script only command.\r
831\r
832 @param[in] CommandName The pointer to the command name.\r
833\r
834 @retval TRUE The command is a script only command.\r
835 @retval FALSE The command is not a script only command.\r
836**/\r
837BOOLEAN\r
47d20b54
MK
838IsScriptOnlyCommand (\r
839 IN CONST CHAR16 *CommandName\r
a405b86d 840 )\r
841{\r
47d20b54
MK
842 if ( IsCommand (CommandName, L"for")\r
843 || IsCommand (CommandName, L"endfor")\r
844 || IsCommand (CommandName, L"if")\r
845 || IsCommand (CommandName, L"else")\r
846 || IsCommand (CommandName, L"endif")\r
847 || IsCommand (CommandName, L"goto"))\r
848 {\r
a405b86d 849 return (TRUE);\r
850 }\r
47d20b54 851\r
a405b86d 852 return (FALSE);\r
853}\r
854\r
a405b86d 855/**\r
856 This function will populate the 2 device path protocol parameters based on the\r
857 global gImageHandle. The DevPath will point to the device path for the handle that has\r
858 loaded image protocol installed on it. The FilePath will point to the device path\r
859 for the file that was loaded.\r
860\r
d08a5464
JP
861 @param[in, out] DevPath On a successful return the device path to the loaded image.\r
862 @param[in, out] FilePath On a successful return the device path to the file.\r
a405b86d 863\r
d08a5464 864 @retval EFI_SUCCESS The 2 device paths were successfully returned.\r
a405b86d 865 @retval other A error from gBS->HandleProtocol.\r
866\r
867 @sa HandleProtocol\r
868**/\r
869EFI_STATUS\r
a405b86d 870GetDevicePathsForImageAndFile (\r
47d20b54
MK
871 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,\r
872 IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath\r
a405b86d 873 )\r
874{\r
47d20b54
MK
875 EFI_STATUS Status;\r
876 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
877 EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;\r
a405b86d 878\r
47d20b54
MK
879 ASSERT (DevPath != NULL);\r
880 ASSERT (FilePath != NULL);\r
a405b86d 881\r
882 Status = gBS->OpenProtocol (\r
47d20b54
MK
883 gImageHandle,\r
884 &gEfiLoadedImageProtocolGuid,\r
885 (VOID **)&LoadedImage,\r
a405b86d 886 gImageHandle,\r
887 NULL,\r
888 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
47d20b54
MK
889 );\r
890 if (!EFI_ERROR (Status)) {\r
891 Status = gBS->OpenProtocol (\r
892 LoadedImage->DeviceHandle,\r
893 &gEfiDevicePathProtocolGuid,\r
894 (VOID **)&ImageDevicePath,\r
895 gImageHandle,\r
896 NULL,\r
897 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
898 );\r
a405b86d 899 if (!EFI_ERROR (Status)) {\r
900 *DevPath = DuplicateDevicePath (ImageDevicePath);\r
901 *FilePath = DuplicateDevicePath (LoadedImage->FilePath);\r
47d20b54
MK
902 gBS->CloseProtocol (\r
903 LoadedImage->DeviceHandle,\r
904 &gEfiDevicePathProtocolGuid,\r
905 gImageHandle,\r
906 NULL\r
907 );\r
908 }\r
909\r
910 gBS->CloseProtocol (\r
911 gImageHandle,\r
912 &gEfiLoadedImageProtocolGuid,\r
913 gImageHandle,\r
914 NULL\r
915 );\r
a405b86d 916 }\r
47d20b54 917\r
a405b86d 918 return (Status);\r
919}\r
920\r
a405b86d 921/**\r
922 Process all Uefi Shell 2.0 command line options.\r
923\r
924 see Uefi Shell 2.0 section 3.2 for full details.\r
925\r
926 the command line must resemble the following:\r
927\r
928 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]\r
929\r
930 ShellOpt-options Options which control the initialization behavior of the shell.\r
931 These options are read from the EFI global variable "ShellOpt"\r
932 and are processed before options or file-name.\r
933\r
934 options Options which control the initialization behavior of the shell.\r
935\r
936 file-name The name of a UEFI shell application or script to be executed\r
937 after initialization is complete. By default, if file-name is\r
938 specified, then -nostartup is implied. Scripts are not supported\r
939 by level 0.\r
940\r
941 file-name-options The command-line options that are passed to file-name when it\r
942 is invoked.\r
943\r
944 This will initialize the ShellInfoObject.ShellInitSettings global variable.\r
945\r
946 @retval EFI_SUCCESS The variable is initialized.\r
947**/\r
948EFI_STATUS\r
47d20b54 949ProcessCommandLine (\r
a405b86d 950 VOID\r
951 )\r
952{\r
23385d63
BJ
953 UINTN Size;\r
954 UINTN LoopVar;\r
955 CHAR16 *CurrentArg;\r
956 CHAR16 *DelayValueStr;\r
957 UINT64 DelayValue;\r
958 EFI_STATUS Status;\r
959 EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation;\r
960\r
961 // `file-name-options` will contain arguments to `file-name` that we don't\r
962 // know about. This would cause ShellCommandLineParse to error, so we parse\r
963 // arguments manually, ignoring those after the first thing that doesn't look\r
964 // like a shell option (which is assumed to be `file-name`).\r
965\r
966 Status = gBS->LocateProtocol (\r
f3a51e98 967 &gEfiUnicodeCollation2ProtocolGuid,\r
23385d63 968 NULL,\r
47d20b54 969 (VOID **)&UnicodeCollation\r
23385d63
BJ
970 );\r
971 if (EFI_ERROR (Status)) {\r
f3a51e98
MH
972 Status = gBS->LocateProtocol (\r
973 &gEfiUnicodeCollationProtocolGuid,\r
974 NULL,\r
47d20b54 975 (VOID **)&UnicodeCollation\r
f3a51e98
MH
976 );\r
977 if (EFI_ERROR (Status)) {\r
978 return Status;\r
979 }\r
23385d63
BJ
980 }\r
981\r
982 // Set default options\r
983 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = FALSE;\r
984 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = FALSE;\r
985 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;\r
986 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = FALSE;\r
987 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = FALSE;\r
988 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = FALSE;\r
989 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = FALSE;\r
990 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = FALSE;\r
991 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = FALSE;\r
dcbdb8bf 992 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = FALSE;\r
e96a5734 993 ShellInfoObject.ShellInitSettings.Delay = PcdGet32 (PcdShellDefaultDelay);\r
23385d63 994\r
e9d19a80
CP
995 //\r
996 // Start LoopVar at 0 to parse only optional arguments at Argv[0]\r
997 // and parse other parameters from Argv[1]. This is for use case that\r
998 // UEFI Shell boot option is created, and OptionalData is provided\r
999 // that starts with shell command-line options.\r
1000 //\r
47d20b54 1001 for (LoopVar = 0; LoopVar < gEfiShellParametersProtocol->Argc; LoopVar++) {\r
23385d63
BJ
1002 CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];\r
1003 if (UnicodeCollation->StriColl (\r
1004 UnicodeCollation,\r
1005 L"-startup",\r
1006 CurrentArg\r
47d20b54
MK
1007 ) == 0)\r
1008 {\r
1009 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE;\r
1010 } else if (UnicodeCollation->StriColl (\r
1011 UnicodeCollation,\r
1012 L"-nostartup",\r
1013 CurrentArg\r
1014 ) == 0)\r
1015 {\r
1016 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = TRUE;\r
1017 } else if (UnicodeCollation->StriColl (\r
1018 UnicodeCollation,\r
1019 L"-noconsoleout",\r
1020 CurrentArg\r
1021 ) == 0)\r
1022 {\r
23385d63 1023 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;\r
47d20b54
MK
1024 } else if (UnicodeCollation->StriColl (\r
1025 UnicodeCollation,\r
1026 L"-noconsolein",\r
1027 CurrentArg\r
1028 ) == 0)\r
1029 {\r
1030 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = TRUE;\r
1031 } else if (UnicodeCollation->StriColl (\r
1032 UnicodeCollation,\r
1033 L"-nointerrupt",\r
1034 CurrentArg\r
1035 ) == 0)\r
1036 {\r
1037 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = TRUE;\r
1038 } else if (UnicodeCollation->StriColl (\r
1039 UnicodeCollation,\r
1040 L"-nomap",\r
1041 CurrentArg\r
1042 ) == 0)\r
1043 {\r
1044 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = TRUE;\r
1045 } else if (UnicodeCollation->StriColl (\r
1046 UnicodeCollation,\r
1047 L"-noversion",\r
1048 CurrentArg\r
1049 ) == 0)\r
1050 {\r
1051 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = TRUE;\r
1052 } else if (UnicodeCollation->StriColl (\r
1053 UnicodeCollation,\r
1054 L"-nonest",\r
1055 CurrentArg\r
1056 ) == 0)\r
1057 {\r
1058 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = TRUE;\r
1059 } else if (UnicodeCollation->StriColl (\r
1060 UnicodeCollation,\r
1061 L"-delay",\r
1062 CurrentArg\r
1063 ) == 0)\r
1064 {\r
1065 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE;\r
23385d63 1066 // Check for optional delay value following "-delay"\r
d3912eb9
ZG
1067 if ((LoopVar + 1) >= gEfiShellParametersProtocol->Argc) {\r
1068 DelayValueStr = NULL;\r
1069 } else {\r
1070 DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];\r
1071 }\r
47d20b54
MK
1072\r
1073 if (DelayValueStr != NULL) {\r
23385d63
BJ
1074 if (*DelayValueStr == L':') {\r
1075 DelayValueStr++;\r
1076 }\r
47d20b54
MK
1077\r
1078 if (!EFI_ERROR (\r
1079 ShellConvertStringToUint64 (\r
1080 DelayValueStr,\r
1081 &DelayValue,\r
1082 FALSE,\r
1083 FALSE\r
1084 )\r
1085 ))\r
1086 {\r
23385d63
BJ
1087 ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;\r
1088 LoopVar++;\r
1089 }\r
1090 }\r
1091 } else if (UnicodeCollation->StriColl (\r
1092 UnicodeCollation,\r
6ca29fa3 1093 L"-exit",\r
23385d63 1094 CurrentArg\r
47d20b54
MK
1095 ) == 0)\r
1096 {\r
1097 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = TRUE;\r
23385d63 1098 } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {\r
d08a5464 1099 // Unrecognized option\r
47d20b54
MK
1100 ShellPrintHiiEx (\r
1101 -1,\r
1102 -1,\r
1103 NULL,\r
23385d63
BJ
1104 STRING_TOKEN (STR_GEN_PROBLEM),\r
1105 ShellInfoObject.HiiHandle,\r
1106 CurrentArg\r
1107 );\r
1108 return EFI_INVALID_PARAMETER;\r
1109 } else {\r
e9d19a80
CP
1110 //\r
1111 // First argument should be Shell.efi image name\r
1112 //\r
1113 if (LoopVar == 0) {\r
1114 continue;\r
1115 }\r
1116\r
e044364b 1117 ShellInfoObject.ShellInitSettings.FileName = NULL;\r
47d20b54 1118 Size = 0;\r
e044364b
MK
1119 //\r
1120 // If first argument contains a space, then add double quotes before the argument\r
1121 //\r
1122 if (StrStr (CurrentArg, L" ") != NULL) {\r
47d20b54 1123 StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);\r
e044364b
MK
1124 if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
1125 return (EFI_OUT_OF_RESOURCES);\r
1126 }\r
1127 }\r
47d20b54
MK
1128\r
1129 StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, CurrentArg, 0);\r
23385d63
BJ
1130 if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
1131 return (EFI_OUT_OF_RESOURCES);\r
1132 }\r
47d20b54 1133\r
23385d63 1134 //\r
e044364b
MK
1135 // If first argument contains a space, then add double quotes after the argument\r
1136 //\r
1137 if (StrStr (CurrentArg, L" ") != NULL) {\r
47d20b54 1138 StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);\r
e044364b
MK
1139 if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
1140 return (EFI_OUT_OF_RESOURCES);\r
1141 }\r
1142 }\r
47d20b54 1143\r
e044364b 1144 //\r
23385d63
BJ
1145 // We found `file-name`.\r
1146 //\r
1147 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
23385d63
BJ
1148 LoopVar++;\r
1149\r
1150 // Add `file-name-options`\r
47d20b54
MK
1151 for (Size = 0; LoopVar < gEfiShellParametersProtocol->Argc; LoopVar++) {\r
1152 ASSERT ((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
e044364b
MK
1153 //\r
1154 // Add a space between arguments\r
1155 //\r
1156 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
47d20b54 1157 StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileOptions, &Size, L" ", 0);\r
e044364b 1158 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
47d20b54 1159 SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);\r
e044364b
MK
1160 return (EFI_OUT_OF_RESOURCES);\r
1161 }\r
1162 }\r
47d20b54 1163\r
e044364b 1164 //\r
6a5033ca 1165 // If an argument contains a space, then add double quotes before the argument\r
e044364b
MK
1166 //\r
1167 if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {\r
47d20b54
MK
1168 StrnCatGrow (\r
1169 &ShellInfoObject.ShellInitSettings.FileOptions,\r
1170 &Size,\r
1171 L"\"",\r
1172 0\r
1173 );\r
e044364b 1174 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
47d20b54 1175 SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);\r
e044364b
MK
1176 return (EFI_OUT_OF_RESOURCES);\r
1177 }\r
23385d63 1178 }\r
47d20b54
MK
1179\r
1180 StrnCatGrow (\r
1181 &ShellInfoObject.ShellInitSettings.FileOptions,\r
1182 &Size,\r
1183 gEfiShellParametersProtocol->Argv[LoopVar],\r
1184 0\r
1185 );\r
23385d63 1186 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
47d20b54 1187 SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);\r
23385d63 1188 return (EFI_OUT_OF_RESOURCES);\r
a405b86d 1189 }\r
47d20b54 1190\r
e044364b 1191 //\r
6a5033ca 1192 // If an argument contains a space, then add double quotes after the argument\r
e044364b
MK
1193 //\r
1194 if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {\r
47d20b54
MK
1195 StrnCatGrow (\r
1196 &ShellInfoObject.ShellInitSettings.FileOptions,\r
1197 &Size,\r
1198 L"\"",\r
1199 0\r
1200 );\r
e044364b 1201 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
47d20b54 1202 SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);\r
e044364b
MK
1203 return (EFI_OUT_OF_RESOURCES);\r
1204 }\r
1205 }\r
a405b86d 1206 }\r
a405b86d 1207 }\r
1208 }\r
1209\r
23385d63 1210 // "-nointerrupt" overrides "-delay"\r
733f138d 1211 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
1212 ShellInfoObject.ShellInitSettings.Delay = 0;\r
a405b86d 1213 }\r
a405b86d 1214\r
23385d63 1215 return EFI_SUCCESS;\r
a405b86d 1216}\r
1217\r
aaf51f08
CC
1218/**\r
1219 Function try to find location of the Startup.nsh file.\r
ba0014b9 1220\r
aaf51f08
CC
1221 The buffer is callee allocated and should be freed by the caller.\r
1222\r
3efa3f3d
DB
1223 @param ImageDevicePath The path to the image for shell. first place to look for the startup script\r
1224 @param FileDevicePath The path to the file for shell. second place to look for the startup script.\r
aaf51f08
CC
1225\r
1226 @retval NULL No Startup.nsh file was found.\r
1227 @return !=NULL Pointer to NULL-terminated path.\r
1228**/\r
1229CHAR16 *\r
1230LocateStartupScript (\r
47d20b54
MK
1231 IN EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,\r
1232 IN EFI_DEVICE_PATH_PROTOCOL *FileDevicePath\r
aaf51f08
CC
1233 )\r
1234{\r
47d20b54
MK
1235 CHAR16 *StartupScriptPath;\r
1236 CHAR16 *TempSpot;\r
1237 CONST CHAR16 *MapName;\r
1238 UINTN Size;\r
aaf51f08
CC
1239\r
1240 StartupScriptPath = NULL;\r
1241 Size = 0;\r
1242\r
1243 //\r
1244 // Try to find 'Startup.nsh' in the directory where the shell itself was launched.\r
1245 //\r
1246 MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath (&ImageDevicePath);\r
ba0014b9 1247 if (MapName != NULL) {\r
aaf51f08 1248 StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, MapName, 0);\r
15de94cf
RN
1249 if (StartupScriptPath == NULL) {\r
1250 //\r
1251 // Do not locate the startup script in sys path when out of resource.\r
1252 //\r
1253 return NULL;\r
1254 }\r
47d20b54 1255\r
aaf51f08
CC
1256 TempSpot = StrStr (StartupScriptPath, L";");\r
1257 if (TempSpot != NULL) {\r
1258 *TempSpot = CHAR_NULL;\r
1259 }\r
1260\r
47d20b54 1261 InternalEfiShellSetEnv (L"homefilesystem", StartupScriptPath, TRUE);\r
c0b1f749 1262\r
aaf51f08
CC
1263 StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, ((FILEPATH_DEVICE_PATH *)FileDevicePath)->PathName, 0);\r
1264 PathRemoveLastItem (StartupScriptPath);\r
1265 StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, mStartupScript, 0);\r
1266 }\r
1267\r
1268 //\r
6a5033ca 1269 // Try to find 'Startup.nsh' in the execution path defined by the environment variable PATH.\r
aaf51f08
CC
1270 //\r
1271 if ((StartupScriptPath == NULL) || EFI_ERROR (ShellIsFile (StartupScriptPath))) {\r
1272 SHELL_FREE_NON_NULL (StartupScriptPath);\r
1273 StartupScriptPath = ShellFindFilePath (mStartupScript);\r
1274 }\r
1275\r
1276 return StartupScriptPath;\r
1277}\r
1278\r
a405b86d 1279/**\r
1280 Handles all interaction with the default startup script.\r
1281\r
1282 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.\r
1283\r
1284 @param ImagePath the path to the image for shell. first place to look for the startup script\r
1285 @param FilePath the path to the file for shell. second place to look for the startup script.\r
1286\r
1287 @retval EFI_SUCCESS the variable is initialized.\r
1288**/\r
1289EFI_STATUS\r
47d20b54
MK
1290DoStartupScript (\r
1291 IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,\r
1292 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
a405b86d 1293 )\r
1294{\r
47d20b54
MK
1295 EFI_STATUS Status;\r
1296 EFI_STATUS CalleeStatus;\r
1297 UINTN Delay;\r
1298 EFI_INPUT_KEY Key;\r
1299 CHAR16 *FileStringPath;\r
1300 CHAR16 *FullFileStringPath;\r
1301 UINTN NewSize;\r
a405b86d 1302\r
1303 Key.UnicodeChar = CHAR_NULL;\r
1304 Key.ScanCode = 0;\r
a405b86d 1305\r
47d20b54 1306 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && (ShellInfoObject.ShellInitSettings.FileName != NULL)) {\r
a405b86d 1307 //\r
1308 // launch something else instead\r
1309 //\r
47d20b54 1310 NewSize = StrSize (ShellInfoObject.ShellInitSettings.FileName);\r
a405b86d 1311 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
47d20b54 1312 NewSize += StrSize (ShellInfoObject.ShellInitSettings.FileOptions) + sizeof (CHAR16);\r
a405b86d 1313 }\r
47d20b54
MK
1314\r
1315 FileStringPath = AllocateZeroPool (NewSize);\r
3c865f20 1316 if (FileStringPath == NULL) {\r
1317 return (EFI_OUT_OF_RESOURCES);\r
1318 }\r
47d20b54
MK
1319\r
1320 StrCpyS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileName);\r
a405b86d 1321 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
47d20b54
MK
1322 StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), L" ", NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1);\r
1323 StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1);\r
a405b86d 1324 }\r
47d20b54
MK
1325\r
1326 Status = RunShellCommand (FileStringPath, &CalleeStatus);\r
490ce43d 1327 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {\r
47d20b54 1328 ShellCommandRegisterExit (gEfiShellProtocol->BatchIsActive (), (UINT64)CalleeStatus);\r
490ce43d 1329 }\r
a405b86d 1330\r
47d20b54
MK
1331 FreePool (FileStringPath);\r
1332 return (Status);\r
a405b86d 1333 }\r
1334\r
1335 //\r
1336 // for shell level 0 we do no scripts\r
1337 // Without the Startup bit overriding we allow for nostartup to prevent scripts\r
1338 //\r
47d20b54
MK
1339 if ( (PcdGet8 (PcdShellSupportLevel) < 1)\r
1340 || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)\r
1341 )\r
1342 {\r
a405b86d 1343 return (EFI_SUCCESS);\r
1344 }\r
1345\r
47d20b54 1346 gST->ConOut->EnableCursor (gST->ConOut, FALSE);\r
a405b86d 1347 //\r
1348 // print out our warning and see if they press a key\r
1349 //\r
284e034f 1350 for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay\r
47d20b54
MK
1351 ; Delay != 0 && EFI_ERROR (Status)\r
1352 ; Delay--\r
1353 )\r
1354 {\r
1355 ShellPrintHiiEx (0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);\r
284e034f 1356 gBS->Stall (1000000);\r
733f138d 1357 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
1358 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
1359 }\r
a405b86d 1360 }\r
47d20b54
MK
1361\r
1362 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);\r
1363 gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
a405b86d 1364\r
1365 //\r
1366 // ESC was pressed\r
1367 //\r
47d20b54 1368 if ((Status == EFI_SUCCESS) && (Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC)) {\r
a405b86d 1369 return (EFI_SUCCESS);\r
1370 }\r
1371\r
aaf51f08
CC
1372 FileStringPath = LocateStartupScript (ImagePath, FilePath);\r
1373 if (FileStringPath != NULL) {\r
47d20b54 1374 FullFileStringPath = FullyQualifyPath (FileStringPath);\r
764c21f4
JD
1375 if (FullFileStringPath == NULL) {\r
1376 Status = RunScriptFile (FileStringPath, NULL, FileStringPath, ShellInfoObject.NewShellParametersProtocol);\r
1377 } else {\r
1378 Status = RunScriptFile (FullFileStringPath, NULL, FullFileStringPath, ShellInfoObject.NewShellParametersProtocol);\r
47d20b54 1379 FreePool (FullFileStringPath);\r
764c21f4 1380 }\r
47d20b54 1381\r
aaf51f08 1382 FreePool (FileStringPath);\r
35b523c2
RN
1383 } else {\r
1384 //\r
1385 // we return success since startup script is not mandatory.\r
1386 //\r
1387 Status = EFI_SUCCESS;\r
a405b86d 1388 }\r
1389\r
a405b86d 1390 return (Status);\r
1391}\r
1392\r
1393/**\r
1394 Function to perform the shell prompt looping. It will do a single prompt,\r
1395 dispatch the result, and then return. It is expected that the caller will\r
1396 call this function in a loop many times.\r
1397\r
1398 @retval EFI_SUCCESS\r
1399 @retval RETURN_ABORTED\r
1400**/\r
1401EFI_STATUS\r
a405b86d 1402DoShellPrompt (\r
1403 VOID\r
1404 )\r
1405{\r
1406 UINTN Column;\r
1407 UINTN Row;\r
1408 CHAR16 *CmdLine;\r
1409 CONST CHAR16 *CurDir;\r
1410 UINTN BufferSize;\r
1411 EFI_STATUS Status;\r
594e780b 1412 LIST_ENTRY OldBufferList;\r
a405b86d 1413\r
47d20b54 1414 CurDir = NULL;\r
a405b86d 1415\r
1416 //\r
1417 // Get screen setting to decide size of the command line buffer\r
1418 //\r
1419 gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);\r
47d20b54
MK
1420 BufferSize = Column * Row * sizeof (CHAR16);\r
1421 CmdLine = AllocateZeroPool (BufferSize);\r
a405b86d 1422 if (CmdLine == NULL) {\r
1423 return EFI_OUT_OF_RESOURCES;\r
1424 }\r
1425\r
47d20b54
MK
1426 SaveBufferList (&OldBufferList);\r
1427 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv (L"cwd");\r
a405b86d 1428\r
1429 //\r
1430 // Prompt for input\r
1431 //\r
1432 gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);\r
1433\r
47d20b54
MK
1434 if ((CurDir != NULL) && (StrLen (CurDir) > 1)) {\r
1435 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
a405b86d 1436 } else {\r
47d20b54 1437 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
a405b86d 1438 }\r
1439\r
1440 //\r
1441 // Read a line from the console\r
1442 //\r
47d20b54 1443 Status = ShellInfoObject.NewEfiShellProtocol->ReadFile (ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);\r
a405b86d 1444\r
1445 //\r
1446 // Null terminate the string and parse it\r
1447 //\r
1448 if (!EFI_ERROR (Status)) {\r
0c83ac57
RN
1449 //\r
1450 // Reset the CTRL-C event just before running the command (yes we ignore the return values)\r
1451 //\r
1452 Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);\r
1453\r
a405b86d 1454 CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;\r
47d20b54 1455 Status = RunCommand (CmdLine);\r
0c83ac57 1456 }\r
a405b86d 1457\r
1458 //\r
1459 // Done with this command\r
1460 //\r
47d20b54 1461 RestoreBufferList (&OldBufferList);\r
a405b86d 1462 FreePool (CmdLine);\r
1463 return Status;\r
1464}\r
1465\r
1466/**\r
1467 Add a buffer to the Buffer To Free List for safely returning buffers to other\r
1468 places without risking letting them modify internal shell information.\r
1469\r
1470 @param Buffer Something to pass to FreePool when the shell is exiting.\r
1471**/\r
47d20b54 1472VOID *\r
b2c036a7 1473AddBufferToFreeList (\r
47d20b54 1474 VOID *Buffer\r
a405b86d 1475 )\r
1476{\r
47d20b54 1477 BUFFER_LIST *BufferListEntry;\r
a405b86d 1478\r
1479 if (Buffer == NULL) {\r
1480 return (NULL);\r
1481 }\r
1482\r
b2c036a7
RN
1483 BufferListEntry = AllocateZeroPool (sizeof (BUFFER_LIST));\r
1484 if (BufferListEntry == NULL) {\r
1485 return NULL;\r
1486 }\r
1487\r
a405b86d 1488 BufferListEntry->Buffer = Buffer;\r
b2c036a7 1489 InsertTailList (&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);\r
a405b86d 1490 return (Buffer);\r
1491}\r
1492\r
14810d6b 1493/**\r
ba0014b9 1494 Create a new buffer list and stores the old one to OldBufferList\r
14810d6b
QS
1495\r
1496 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.\r
1497**/\r
1498VOID\r
1499SaveBufferList (\r
47d20b54 1500 OUT LIST_ENTRY *OldBufferList\r
14810d6b
QS
1501 )\r
1502{\r
1503 CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));\r
1504 InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);\r
1505}\r
1506\r
1507/**\r
1508 Restore previous nodes into BufferToFreeList .\r
1509\r
1510 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.\r
1511**/\r
1512VOID\r
1513RestoreBufferList (\r
47d20b54 1514 IN OUT LIST_ENTRY *OldBufferList\r
14810d6b
QS
1515 )\r
1516{\r
1517 FreeBufferList (&ShellInfoObject.BufferToFreeList);\r
1518 CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));\r
1519}\r
1520\r
a405b86d 1521/**\r
1522 Add a buffer to the Line History List\r
1523\r
1524 @param Buffer The line buffer to add.\r
1525**/\r
1526VOID\r
47d20b54
MK
1527AddLineToCommandHistory (\r
1528 IN CONST CHAR16 *Buffer\r
a405b86d 1529 )\r
1530{\r
47d20b54
MK
1531 BUFFER_LIST *Node;\r
1532 BUFFER_LIST *Walker;\r
a4f138a4
QS
1533 UINT16 MaxHistoryCmdCount;\r
1534 UINT16 Count;\r
dcbdb8bf 1535\r
47d20b54
MK
1536 Count = 0;\r
1537 MaxHistoryCmdCount = PcdGet16 (PcdShellMaxHistoryCommandCount);\r
ba0014b9 1538\r
a4f138a4 1539 if (MaxHistoryCmdCount == 0) {\r
47d20b54 1540 return;\r
a4f138a4
QS
1541 }\r
1542\r
47d20b54 1543 Node = AllocateZeroPool (sizeof (BUFFER_LIST));\r
b2c036a7
RN
1544 if (Node == NULL) {\r
1545 return;\r
1546 }\r
1547\r
1548 Node->Buffer = AllocateCopyPool (StrSize (Buffer), Buffer);\r
1549 if (Node->Buffer == NULL) {\r
1550 FreePool (Node);\r
1551 return;\r
1552 }\r
a405b86d 1553\r
47d20b54
MK
1554 for ( Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link)\r
1555 ; !IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)\r
1556 ; Walker = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)\r
1557 )\r
1558 {\r
a4f138a4
QS
1559 Count++;\r
1560 }\r
47d20b54
MK
1561\r
1562 if (Count < MaxHistoryCmdCount) {\r
1563 InsertTailList (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
a4f138a4 1564 } else {\r
47d20b54
MK
1565 Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link);\r
1566 RemoveEntryList (&Walker->Link);\r
a4f138a4 1567 if (Walker->Buffer != NULL) {\r
47d20b54 1568 FreePool (Walker->Buffer);\r
a4f138a4 1569 }\r
47d20b54
MK
1570\r
1571 FreePool (Walker);\r
1572 InsertTailList (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
a4f138a4 1573 }\r
a405b86d 1574}\r
1575\r
1576/**\r
1577 Checks if a string is an alias for another command. If yes, then it replaces the alias name\r
1578 with the correct command name.\r
1579\r
4ff7e37b
ED
1580 @param[in, out] CommandString Upon entry the potential alias. Upon return the\r
1581 command name if it was an alias. If it was not\r
1582 an alias it will be unchanged. This function may\r
1583 change the buffer to fit the command name.\r
a405b86d 1584\r
1585 @retval EFI_SUCCESS The name was changed.\r
1586 @retval EFI_SUCCESS The name was not an alias.\r
1587 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
1588**/\r
1589EFI_STATUS\r
47d20b54
MK
1590ShellConvertAlias (\r
1591 IN OUT CHAR16 **CommandString\r
a405b86d 1592 )\r
1593{\r
1594 CONST CHAR16 *NewString;\r
1595\r
47d20b54 1596 NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias (*CommandString, NULL);\r
a405b86d 1597 if (NewString == NULL) {\r
1598 return (EFI_SUCCESS);\r
1599 }\r
47d20b54
MK
1600\r
1601 FreePool (*CommandString);\r
1602 *CommandString = AllocateCopyPool (StrSize (NewString), NewString);\r
a405b86d 1603 if (*CommandString == NULL) {\r
1604 return (EFI_OUT_OF_RESOURCES);\r
1605 }\r
47d20b54 1606\r
a405b86d 1607 return (EFI_SUCCESS);\r
1608}\r
1609\r
d5b5440b
JC
1610/**\r
1611 This function will eliminate unreplaced (and therefore non-found) environment variables.\r
1612\r
1613 @param[in,out] CmdLine The command line to update.\r
1614**/\r
1615EFI_STATUS\r
47d20b54
MK
1616StripUnreplacedEnvironmentVariables (\r
1617 IN OUT CHAR16 *CmdLine\r
d5b5440b
JC
1618 )\r
1619{\r
47d20b54
MK
1620 CHAR16 *FirstPercent;\r
1621 CHAR16 *FirstQuote;\r
1622 CHAR16 *SecondPercent;\r
1623 CHAR16 *SecondQuote;\r
1624 CHAR16 *CurrentLocator;\r
1625\r
1626 for (CurrentLocator = CmdLine; CurrentLocator != NULL; ) {\r
1627 FirstQuote = FindNextInstance (CurrentLocator, L"\"", TRUE);\r
1628 FirstPercent = FindNextInstance (CurrentLocator, L"%", TRUE);\r
1629 SecondPercent = FirstPercent != NULL ? FindNextInstance (FirstPercent+1, L"%", TRUE) : NULL;\r
1630 if ((FirstPercent == NULL) || (SecondPercent == NULL)) {\r
d5b5440b 1631 //\r
d08a5464 1632 // If we ever don't have 2 % we are done.\r
d5b5440b
JC
1633 //\r
1634 break;\r
1635 }\r
1636\r
47d20b54
MK
1637 if ((FirstQuote != NULL) && (FirstQuote < FirstPercent)) {\r
1638 SecondQuote = FindNextInstance (FirstQuote+1, L"\"", TRUE);\r
d5b5440b
JC
1639 //\r
1640 // Quote is first found\r
1641 //\r
a95cf8f0 1642\r
d5b5440b
JC
1643 if (SecondQuote < FirstPercent) {\r
1644 //\r
1645 // restart after the pair of "\r
1646 //\r
1647 CurrentLocator = SecondQuote + 1;\r
47d20b54
MK
1648 } else {\r
1649 /* FirstPercent < SecondQuote */\r
d5b5440b
JC
1650 //\r
1651 // Restart on the first percent\r
1652 //\r
1653 CurrentLocator = FirstPercent;\r
1654 }\r
47d20b54 1655\r
d5b5440b
JC
1656 continue;\r
1657 }\r
ba0014b9 1658\r
47d20b54
MK
1659 if ((FirstQuote == NULL) || (SecondPercent < FirstQuote)) {\r
1660 if (IsValidEnvironmentVariableName (FirstPercent, SecondPercent)) {\r
42435671
QS
1661 //\r
1662 // We need to remove from FirstPercent to SecondPercent\r
1663 //\r
47d20b54 1664 CopyMem (FirstPercent, SecondPercent + 1, StrSize (SecondPercent + 1));\r
42435671 1665 //\r
d08a5464 1666 // don't need to update the locator. both % characters are gone.\r
42435671
QS
1667 //\r
1668 } else {\r
1669 CurrentLocator = SecondPercent + 1;\r
1670 }\r
47d20b54 1671\r
d5b5440b
JC
1672 continue;\r
1673 }\r
47d20b54 1674\r
d5b5440b
JC
1675 CurrentLocator = FirstQuote;\r
1676 }\r
47d20b54 1677\r
d5b5440b
JC
1678 return (EFI_SUCCESS);\r
1679}\r
1680\r
a405b86d 1681/**\r
1682 Function allocates a new command line and replaces all instances of environment\r
1683 variable names that are correctly preset to their values.\r
1684\r
1685 If the return value is not NULL the memory must be caller freed.\r
1686\r
1687 @param[in] OriginalCommandLine The original command line\r
1688\r
d08a5464 1689 @retval NULL An error occurred.\r
a405b86d 1690 @return The new command line with no environment variables present.\r
1691**/\r
47d20b54 1692CHAR16 *\r
a405b86d 1693ShellConvertVariables (\r
47d20b54 1694 IN CONST CHAR16 *OriginalCommandLine\r
a405b86d 1695 )\r
1696{\r
47d20b54
MK
1697 CONST CHAR16 *MasterEnvList;\r
1698 UINTN NewSize;\r
1699 CHAR16 *NewCommandLine1;\r
1700 CHAR16 *NewCommandLine2;\r
1701 CHAR16 *Temp;\r
1702 UINTN ItemSize;\r
1703 CHAR16 *ItemTemp;\r
1704 SCRIPT_FILE *CurrentScriptFile;\r
1705 ALIAS_LIST *AliasListNode;\r
1706\r
1707 ASSERT (OriginalCommandLine != NULL);\r
a405b86d 1708\r
1709 ItemSize = 0;\r
47d20b54
MK
1710 NewSize = StrSize (OriginalCommandLine);\r
1711 CurrentScriptFile = ShellCommandGetCurrentScriptFile ();\r
a405b86d 1712 Temp = NULL;\r
1713\r
47d20b54 1714 /// @todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?\r
a405b86d 1715\r
1716 //\r
1717 // calculate the size required for the post-conversion string...\r
1718 //\r
1719 if (CurrentScriptFile != NULL) {\r
47d20b54
MK
1720 for (AliasListNode = (ALIAS_LIST *)GetFirstNode (&CurrentScriptFile->SubstList)\r
1721 ; !IsNull (&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1722 ; AliasListNode = (ALIAS_LIST *)GetNextNode (&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1723 )\r
1724 {\r
1725 for (Temp = StrStr (OriginalCommandLine, AliasListNode->Alias)\r
1726 ; Temp != NULL\r
1727 ; Temp = StrStr (Temp+1, AliasListNode->Alias)\r
1728 )\r
1729 {\r
a405b86d 1730 //\r
d08a5464 1731 // we need a preceding and if there is space no ^ preceding (if no space ignore)\r
a405b86d 1732 //\r
47d20b54
MK
1733 if ((((Temp-OriginalCommandLine) > 2) && (*(Temp-2) != L'^')) || ((Temp-OriginalCommandLine) <= 2)) {\r
1734 NewSize += StrSize (AliasListNode->CommandString);\r
a405b86d 1735 }\r
1736 }\r
1737 }\r
1738 }\r
1739\r
47d20b54
MK
1740 for (MasterEnvList = EfiShellGetEnv (NULL)\r
1741 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL // && *(MasterEnvList+1) != CHAR_NULL\r
1742 ; MasterEnvList += StrLen (MasterEnvList) + 1\r
1743 )\r
1744 {\r
1745 if (StrSize (MasterEnvList) > ItemSize) {\r
1746 ItemSize = StrSize (MasterEnvList);\r
a405b86d 1747 }\r
47d20b54
MK
1748\r
1749 for (Temp = StrStr (OriginalCommandLine, MasterEnvList)\r
1750 ; Temp != NULL\r
1751 ; Temp = StrStr (Temp+1, MasterEnvList)\r
1752 )\r
1753 {\r
a405b86d 1754 //\r
d08a5464 1755 // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)\r
a405b86d 1756 //\r
47d20b54
MK
1757 if ((*(Temp-1) == L'%') && (*(Temp+StrLen (MasterEnvList)) == L'%') &&\r
1758 ((((Temp-OriginalCommandLine) > 2) && (*(Temp-2) != L'^')) || ((Temp-OriginalCommandLine) <= 2)))\r
1759 {\r
1760 NewSize += StrSize (EfiShellGetEnv (MasterEnvList));\r
a405b86d 1761 }\r
1762 }\r
1763 }\r
1764\r
a405b86d 1765 //\r
1766 // now do the replacements...\r
1767 //\r
2a6ede28 1768 NewCommandLine1 = AllocateZeroPool (NewSize);\r
47d20b54
MK
1769 NewCommandLine2 = AllocateZeroPool (NewSize);\r
1770 ItemTemp = AllocateZeroPool (ItemSize+(2*sizeof (CHAR16)));\r
1771 if ((NewCommandLine1 == NULL) || (NewCommandLine2 == NULL) || (ItemTemp == NULL)) {\r
1772 SHELL_FREE_NON_NULL (NewCommandLine1);\r
1773 SHELL_FREE_NON_NULL (NewCommandLine2);\r
1774 SHELL_FREE_NON_NULL (ItemTemp);\r
3c865f20 1775 return (NULL);\r
1776 }\r
47d20b54 1777\r
2a6ede28
JW
1778 CopyMem (NewCommandLine1, OriginalCommandLine, StrSize (OriginalCommandLine));\r
1779\r
47d20b54
MK
1780 for (MasterEnvList = EfiShellGetEnv (NULL)\r
1781 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL\r
1782 ; MasterEnvList += StrLen (MasterEnvList) + 1\r
1783 )\r
1784 {\r
1785 StrCpyS (\r
1786 ItemTemp,\r
1787 ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),\r
1788 L"%"\r
1789 );\r
1790 StrCatS (\r
1791 ItemTemp,\r
1792 ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),\r
1793 MasterEnvList\r
1794 );\r
1795 StrCatS (\r
1796 ItemTemp,\r
1797 ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),\r
1798 L"%"\r
1799 );\r
1800 ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv (MasterEnvList), TRUE, FALSE);\r
1801 StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);\r
a405b86d 1802 }\r
47d20b54 1803\r
a405b86d 1804 if (CurrentScriptFile != NULL) {\r
47d20b54
MK
1805 for (AliasListNode = (ALIAS_LIST *)GetFirstNode (&CurrentScriptFile->SubstList)\r
1806 ; !IsNull (&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1807 ; AliasListNode = (ALIAS_LIST *)GetNextNode (&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1808 )\r
1809 {\r
1810 ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);\r
1811 StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);\r
a405b86d 1812 }\r
1813 }\r
1814\r
fdd52bde 1815 //\r
d08a5464 1816 // Remove non-existent environment variables\r
fdd52bde 1817 //\r
47d20b54 1818 StripUnreplacedEnvironmentVariables (NewCommandLine1);\r
fdd52bde 1819\r
df07baea
JC
1820 //\r
1821 // Now cleanup any straggler intentionally ignored "%" characters\r
1822 //\r
47d20b54
MK
1823 ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);\r
1824 StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);\r
ba0014b9 1825\r
47d20b54
MK
1826 FreePool (NewCommandLine2);\r
1827 FreePool (ItemTemp);\r
a405b86d 1828\r
1829 return (NewCommandLine1);\r
1830}\r
1831\r
1832/**\r
1833 Internal function to run a command line with pipe usage.\r
1834\r
1835 @param[in] CmdLine The pointer to the command line.\r
1836 @param[in] StdIn The pointer to the Standard input.\r
1837 @param[in] StdOut The pointer to the Standard output.\r
1838\r
1839 @retval EFI_SUCCESS The split command is executed successfully.\r
1840 @retval other Some error occurs when executing the split command.\r
1841**/\r
1842EFI_STATUS\r
47d20b54 1843RunSplitCommand (\r
a405b86d 1844 IN CONST CHAR16 *CmdLine,\r
1bd0bf15
LE
1845 IN SHELL_FILE_HANDLE StdIn,\r
1846 IN SHELL_FILE_HANDLE StdOut\r
a405b86d 1847 )\r
1848{\r
47d20b54
MK
1849 EFI_STATUS Status;\r
1850 CHAR16 *NextCommandLine;\r
1851 CHAR16 *OurCommandLine;\r
1852 UINTN Size1;\r
1853 UINTN Size2;\r
1854 SPLIT_LIST *Split;\r
1855 SHELL_FILE_HANDLE TempFileHandle;\r
1856 BOOLEAN Unicode;\r
a405b86d 1857\r
47d20b54 1858 ASSERT (StdOut == NULL);\r
a405b86d 1859\r
47d20b54 1860 ASSERT (StrStr (CmdLine, L"|") != NULL);\r
a405b86d 1861\r
1862 Status = EFI_SUCCESS;\r
1863 NextCommandLine = NULL;\r
1864 OurCommandLine = NULL;\r
1865 Size1 = 0;\r
1866 Size2 = 0;\r
1867\r
47d20b54
MK
1868 NextCommandLine = StrnCatGrow (&NextCommandLine, &Size1, StrStr (CmdLine, L"|")+1, 0);\r
1869 OurCommandLine = StrnCatGrow (&OurCommandLine, &Size2, CmdLine, StrStr (CmdLine, L"|") - CmdLine);\r
532691c8 1870\r
47d20b54
MK
1871 if ((NextCommandLine == NULL) || (OurCommandLine == NULL)) {\r
1872 SHELL_FREE_NON_NULL (OurCommandLine);\r
1873 SHELL_FREE_NON_NULL (NextCommandLine);\r
532691c8 1874 return (EFI_OUT_OF_RESOURCES);\r
47d20b54
MK
1875 } else if ((StrStr (OurCommandLine, L"|") != NULL) || (Size1 == 0) || (Size2 == 0)) {\r
1876 SHELL_FREE_NON_NULL (OurCommandLine);\r
1877 SHELL_FREE_NON_NULL (NextCommandLine);\r
19216573 1878 return (EFI_INVALID_PARAMETER);\r
47d20b54
MK
1879 } else if ((NextCommandLine[0] == L'a') &&\r
1880 ((NextCommandLine[1] == L' ') || (NextCommandLine[1] == CHAR_NULL))\r
1881 )\r
1882 {\r
1883 CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0]));\r
a5382ce0 1884 while (NextCommandLine[0] == L' ') {\r
47d20b54 1885 CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0]));\r
a5382ce0 1886 }\r
47d20b54 1887\r
a5382ce0 1888 if (NextCommandLine[0] == CHAR_NULL) {\r
47d20b54
MK
1889 SHELL_FREE_NON_NULL (OurCommandLine);\r
1890 SHELL_FREE_NON_NULL (NextCommandLine);\r
a5382ce0
QS
1891 return (EFI_INVALID_PARAMETER);\r
1892 }\r
47d20b54 1893\r
a405b86d 1894 Unicode = FALSE;\r
1895 } else {\r
1896 Unicode = TRUE;\r
1897 }\r
1898\r
a405b86d 1899 //\r
1900 // make a SPLIT_LIST item and add to list\r
1901 //\r
47d20b54 1902 Split = AllocateZeroPool (sizeof (SPLIT_LIST));\r
b2c036a7
RN
1903 if (Split == NULL) {\r
1904 return EFI_OUT_OF_RESOURCES;\r
1905 }\r
a405b86d 1906\r
47d20b54
MK
1907 Split->SplitStdIn = StdIn;\r
1908 Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (Unicode), NULL);\r
1909 ASSERT (Split->SplitStdOut != NULL);\r
1910 InsertHeadList (&ShellInfoObject.SplitList.Link, &Split->Link);\r
1911\r
1912 Status = RunCommand (OurCommandLine);\r
a405b86d 1913\r
1914 //\r
1915 // move the output from the first to the in to the second.\r
1916 //\r
47d20b54 1917 TempFileHandle = Split->SplitStdOut;\r
a405b86d 1918 if (Split->SplitStdIn == StdIn) {\r
1919 Split->SplitStdOut = NULL;\r
1920 } else {\r
47d20b54 1921 Split->SplitStdOut = Split->SplitStdIn;\r
a405b86d 1922 }\r
47d20b54
MK
1923\r
1924 Split->SplitStdIn = TempFileHandle;\r
1bd0bf15 1925 ShellInfoObject.NewEfiShellProtocol->SetFilePosition (Split->SplitStdIn, 0);\r
a405b86d 1926\r
47d20b54
MK
1927 if (!EFI_ERROR (Status)) {\r
1928 Status = RunCommand (NextCommandLine);\r
a405b86d 1929 }\r
1930\r
1931 //\r
1932 // remove the top level from the ScriptList\r
1933 //\r
47d20b54
MK
1934 ASSERT ((SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link) == Split);\r
1935 RemoveEntryList (&Split->Link);\r
a405b86d 1936\r
1937 //\r
1938 // Note that the original StdIn is now the StdOut...\r
1939 //\r
bd3fc813 1940 if (Split->SplitStdOut != NULL) {\r
1bd0bf15 1941 ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdOut);\r
a405b86d 1942 }\r
47d20b54 1943\r
a405b86d 1944 if (Split->SplitStdIn != NULL) {\r
1bd0bf15 1945 ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdIn);\r
a405b86d 1946 }\r
1947\r
47d20b54
MK
1948 FreePool (Split);\r
1949 FreePool (NextCommandLine);\r
1950 FreePool (OurCommandLine);\r
a405b86d 1951\r
1952 return (Status);\r
1953}\r
1954\r
1ef61d03 1955/**\r
ba0014b9 1956 Take the original command line, substitute any variables, free\r
51429264 1957 the original string, return the modified copy.\r
1ef61d03 1958\r
51429264 1959 @param[in] CmdLine pointer to the command line to update.\r
1ef61d03 1960\r
51429264
SQ
1961 @retval EFI_SUCCESS the function was successful.\r
1962 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.\r
1ef61d03
JC
1963**/\r
1964EFI_STATUS\r
47d20b54
MK
1965ShellSubstituteVariables (\r
1966 IN CHAR16 **CmdLine\r
1ef61d03
JC
1967 )\r
1968{\r
47d20b54
MK
1969 CHAR16 *NewCmdLine;\r
1970\r
1971 NewCmdLine = ShellConvertVariables (*CmdLine);\r
1972 SHELL_FREE_NON_NULL (*CmdLine);\r
1ef61d03
JC
1973 if (NewCmdLine == NULL) {\r
1974 return (EFI_OUT_OF_RESOURCES);\r
1975 }\r
47d20b54 1976\r
1ef61d03
JC
1977 *CmdLine = NewCmdLine;\r
1978 return (EFI_SUCCESS);\r
1979}\r
1980\r
ca53c0af 1981/**\r
ba0014b9 1982 Take the original command line, substitute any alias in the first group of space delimited characters, free\r
51429264 1983 the original string, return the modified copy.\r
ca53c0af 1984\r
51429264 1985 @param[in] CmdLine pointer to the command line to update.\r
ca53c0af 1986\r
51429264
SQ
1987 @retval EFI_SUCCESS the function was successful.\r
1988 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.\r
ca53c0af
JC
1989**/\r
1990EFI_STATUS\r
47d20b54
MK
1991ShellSubstituteAliases (\r
1992 IN CHAR16 **CmdLine\r
ca53c0af
JC
1993 )\r
1994{\r
1995 CHAR16 *NewCmdLine;\r
1996 CHAR16 *CommandName;\r
1997 EFI_STATUS Status;\r
1998 UINTN PostAliasSize;\r
ca53c0af 1999\r
47d20b54
MK
2000 ASSERT (CmdLine != NULL);\r
2001 ASSERT (*CmdLine != NULL);\r
ca53c0af
JC
2002\r
2003 CommandName = NULL;\r
47d20b54
MK
2004 if (StrStr ((*CmdLine), L" ") == NULL) {\r
2005 StrnCatGrow (&CommandName, NULL, (*CmdLine), 0);\r
ca53c0af 2006 } else {\r
47d20b54 2007 StrnCatGrow (&CommandName, NULL, (*CmdLine), StrStr ((*CmdLine), L" ") - (*CmdLine));\r
ca53c0af
JC
2008 }\r
2009\r
2010 //\r
2011 // This cannot happen 'inline' since the CmdLine can need extra space.\r
2012 //\r
2013 NewCmdLine = NULL;\r
47d20b54 2014 if (!ShellCommandIsCommandOnList (CommandName)) {\r
ca53c0af
JC
2015 //\r
2016 // Convert via alias\r
2017 //\r
47d20b54
MK
2018 Status = ShellConvertAlias (&CommandName);\r
2019 if (EFI_ERROR (Status)) {\r
ca53c0af
JC
2020 return (Status);\r
2021 }\r
47d20b54 2022\r
ca53c0af 2023 PostAliasSize = 0;\r
47d20b54 2024 NewCmdLine = StrnCatGrow (&NewCmdLine, &PostAliasSize, CommandName, 0);\r
ca53c0af 2025 if (NewCmdLine == NULL) {\r
47d20b54
MK
2026 SHELL_FREE_NON_NULL (CommandName);\r
2027 SHELL_FREE_NON_NULL (*CmdLine);\r
ca53c0af
JC
2028 return (EFI_OUT_OF_RESOURCES);\r
2029 }\r
47d20b54
MK
2030\r
2031 NewCmdLine = StrnCatGrow (&NewCmdLine, &PostAliasSize, StrStr ((*CmdLine), L" "), 0);\r
ca53c0af 2032 if (NewCmdLine == NULL) {\r
47d20b54
MK
2033 SHELL_FREE_NON_NULL (CommandName);\r
2034 SHELL_FREE_NON_NULL (*CmdLine);\r
ca53c0af
JC
2035 return (EFI_OUT_OF_RESOURCES);\r
2036 }\r
2037 } else {\r
47d20b54 2038 NewCmdLine = StrnCatGrow (&NewCmdLine, NULL, (*CmdLine), 0);\r
ca53c0af
JC
2039 }\r
2040\r
47d20b54
MK
2041 SHELL_FREE_NON_NULL (*CmdLine);\r
2042 SHELL_FREE_NON_NULL (CommandName);\r
ba0014b9 2043\r
ca53c0af
JC
2044 //\r
2045 // re-assign the passed in double pointer to point to our newly allocated buffer\r
2046 //\r
2047 *CmdLine = NewCmdLine;\r
2048\r
2049 return (EFI_SUCCESS);\r
2050}\r
2051\r
6ba2921d
JC
2052/**\r
2053 Takes the Argv[0] part of the command line and determine the meaning of it.\r
e7831c90 2054\r
51429264 2055 @param[in] CmdName pointer to the command line to update.\r
ba0014b9 2056\r
51429264
SQ
2057 @retval Internal_Command The name is an internal command.\r
2058 @retval File_Sys_Change the name is a file system change.\r
2059 @retval Script_File_Name the name is a NSH script file.\r
2060 @retval Unknown_Invalid the name is unknown.\r
2061 @retval Efi_Application the name is an application (.EFI).\r
6ba2921d
JC
2062**/\r
2063SHELL_OPERATION_TYPES\r
47d20b54
MK
2064GetOperationType (\r
2065 IN CONST CHAR16 *CmdName\r
6ba2921d
JC
2066 )\r
2067{\r
47d20b54
MK
2068 CHAR16 *FileWithPath;\r
2069 CONST CHAR16 *TempLocation;\r
2070 CONST CHAR16 *TempLocation2;\r
6ba2921d
JC
2071\r
2072 FileWithPath = NULL;\r
2073 //\r
2074 // test for an internal command.\r
2075 //\r
47d20b54 2076 if (ShellCommandIsCommandOnList (CmdName)) {\r
51429264 2077 return (Internal_Command);\r
6ba2921d
JC
2078 }\r
2079\r
2080 //\r
fc4c7b30 2081 // Test for file system change request. anything ending with first : and cant have spaces.\r
6ba2921d 2082 //\r
47d20b54
MK
2083 if (CmdName[(StrLen (CmdName)-1)] == L':') {\r
2084 if ( (StrStr (CmdName, L" ") != NULL)\r
2085 || (StrLen (StrStr (CmdName, L":")) > 1)\r
2086 )\r
2087 {\r
51429264 2088 return (Unknown_Invalid);\r
6ba2921d 2089 }\r
47d20b54 2090\r
51429264 2091 return (File_Sys_Change);\r
6ba2921d
JC
2092 }\r
2093\r
2094 //\r
2095 // Test for a file\r
2096 //\r
47d20b54 2097 if ((FileWithPath = ShellFindFilePathEx (CmdName, mExecutableExtensions)) != NULL) {\r
6ba2921d
JC
2098 //\r
2099 // See if that file has a script file extension\r
2100 //\r
47d20b54
MK
2101 if (StrLen (FileWithPath) > 4) {\r
2102 TempLocation = FileWithPath+StrLen (FileWithPath)-4;\r
6ba2921d 2103 TempLocation2 = mScriptExtension;\r
47d20b54
MK
2104 if (StringNoCaseCompare ((VOID *)(&TempLocation), (VOID *)(&TempLocation2)) == 0) {\r
2105 SHELL_FREE_NON_NULL (FileWithPath);\r
51429264 2106 return (Script_File_Name);\r
6ba2921d
JC
2107 }\r
2108 }\r
2109\r
2110 //\r
2111 // Was a file, but not a script. we treat this as an application.\r
2112 //\r
47d20b54 2113 SHELL_FREE_NON_NULL (FileWithPath);\r
51429264 2114 return (Efi_Application);\r
6ba2921d 2115 }\r
ba0014b9 2116\r
47d20b54 2117 SHELL_FREE_NON_NULL (FileWithPath);\r
6ba2921d
JC
2118 //\r
2119 // No clue what this is... return invalid flag...\r
2120 //\r
51429264 2121 return (Unknown_Invalid);\r
6ba2921d
JC
2122}\r
2123\r
de4ebdcf
SQ
2124/**\r
2125 Determine if the first item in a command line is valid.\r
2126\r
2127 @param[in] CmdLine The command line to parse.\r
2128\r
2129 @retval EFI_SUCCESS The item is valid.\r
2130 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
2131 @retval EFI_NOT_FOUND The operation type is unknown or invalid.\r
2132**/\r
ba0014b9 2133EFI_STATUS\r
47d20b54
MK
2134IsValidSplit (\r
2135 IN CONST CHAR16 *CmdLine\r
6bd64463
JC
2136 )\r
2137{\r
47d20b54
MK
2138 CHAR16 *Temp;\r
2139 CHAR16 *FirstParameter;\r
2140 CHAR16 *TempWalker;\r
2141 EFI_STATUS Status;\r
6bd64463 2142\r
47d20b54 2143 Temp = NULL;\r
6bd64463 2144\r
47d20b54 2145 Temp = StrnCatGrow (&Temp, NULL, CmdLine, 0);\r
6bd64463
JC
2146 if (Temp == NULL) {\r
2147 return (EFI_OUT_OF_RESOURCES);\r
2148 }\r
2149\r
47d20b54 2150 FirstParameter = StrStr (Temp, L"|");\r
6bd64463
JC
2151 if (FirstParameter != NULL) {\r
2152 *FirstParameter = CHAR_NULL;\r
2153 }\r
2154\r
2155 FirstParameter = NULL;\r
2156\r
2157 //\r
2158 // Process the command line\r
2159 //\r
47d20b54 2160 Status = ProcessCommandLineToFinal (&Temp);\r
6bd64463 2161\r
47d20b54
MK
2162 if (!EFI_ERROR (Status)) {\r
2163 FirstParameter = AllocateZeroPool (StrSize (CmdLine));\r
6bd64463 2164 if (FirstParameter == NULL) {\r
47d20b54 2165 SHELL_FREE_NON_NULL (Temp);\r
6bd64463
JC
2166 return (EFI_OUT_OF_RESOURCES);\r
2167 }\r
47d20b54
MK
2168\r
2169 TempWalker = (CHAR16 *)Temp;\r
2170 if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CmdLine), TRUE))) {\r
2171 if (GetOperationType (FirstParameter) == Unknown_Invalid) {\r
2172 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2173 SetLastError (SHELL_NOT_FOUND);\r
14030c5c
JC
2174 Status = EFI_NOT_FOUND;\r
2175 }\r
6bd64463
JC
2176 }\r
2177 }\r
2178\r
47d20b54
MK
2179 SHELL_FREE_NON_NULL (Temp);\r
2180 SHELL_FREE_NON_NULL (FirstParameter);\r
6bd64463
JC
2181 return Status;\r
2182}\r
2183\r
2184/**\r
51429264 2185 Determine if a command line contains with a split contains only valid commands.\r
6bd64463
JC
2186\r
2187 @param[in] CmdLine The command line to parse.\r
2188\r
2189 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.\r
2190 @retval EFI_ABORTED CmdLine has at least one invalid command or application.\r
2191**/\r
2192EFI_STATUS\r
47d20b54
MK
2193VerifySplit (\r
2194 IN CONST CHAR16 *CmdLine\r
6bd64463
JC
2195 )\r
2196{\r
2197 CONST CHAR16 *TempSpot;\r
2198 EFI_STATUS Status;\r
2199\r
2200 //\r
a5382ce0 2201 // If this was the only item, then get out\r
6bd64463 2202 //\r
47d20b54 2203 if (!ContainsSplit (CmdLine)) {\r
a5382ce0 2204 return (EFI_SUCCESS);\r
6bd64463
JC
2205 }\r
2206\r
2207 //\r
a5382ce0 2208 // Verify up to the pipe or end character\r
6bd64463 2209 //\r
47d20b54
MK
2210 Status = IsValidSplit (CmdLine);\r
2211 if (EFI_ERROR (Status)) {\r
a5382ce0 2212 return (Status);\r
6bd64463
JC
2213 }\r
2214\r
2215 //\r
2216 // recurse to verify the next item\r
2217 //\r
47d20b54
MK
2218 TempSpot = FindFirstCharacter (CmdLine, L"|", L'^') + 1;\r
2219 if ((*TempSpot == L'a') &&\r
2220 ((*(TempSpot + 1) == L' ') || (*(TempSpot + 1) == CHAR_NULL))\r
2221 )\r
2222 {\r
a5382ce0
QS
2223 // If it's an ASCII pipe '|a'\r
2224 TempSpot += 1;\r
2225 }\r
ba0014b9 2226\r
47d20b54 2227 return (VerifySplit (TempSpot));\r
6bd64463
JC
2228}\r
2229\r
680db511 2230/**\r
e7831c90
JC
2231 Process a split based operation.\r
2232\r
a308e058 2233 @param[in] CmdLine pointer to the command line to process\r
e7831c90
JC
2234\r
2235 @retval EFI_SUCCESS The operation was successful\r
d08a5464 2236 @return an error occurred.\r
680db511
JC
2237**/\r
2238EFI_STATUS\r
47d20b54
MK
2239ProcessNewSplitCommandLine (\r
2240 IN CONST CHAR16 *CmdLine\r
680db511
JC
2241 )\r
2242{\r
47d20b54
MK
2243 SPLIT_LIST *Split;\r
2244 EFI_STATUS Status;\r
680db511 2245\r
47d20b54
MK
2246 Status = VerifySplit (CmdLine);\r
2247 if (EFI_ERROR (Status)) {\r
6bd64463
JC
2248 return (Status);\r
2249 }\r
2250\r
680db511
JC
2251 Split = NULL;\r
2252\r
2253 //\r
2254 // are we in an existing split???\r
2255 //\r
47d20b54
MK
2256 if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) {\r
2257 Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link);\r
680db511
JC
2258 }\r
2259\r
2260 if (Split == NULL) {\r
47d20b54 2261 Status = RunSplitCommand (CmdLine, NULL, NULL);\r
680db511 2262 } else {\r
47d20b54 2263 Status = RunSplitCommand (CmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
680db511 2264 }\r
47d20b54
MK
2265\r
2266 if (EFI_ERROR (Status)) {\r
2267 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);\r
680db511 2268 }\r
47d20b54 2269\r
680db511
JC
2270 return (Status);\r
2271}\r
daf70584
JC
2272\r
2273/**\r
51429264 2274 Handle a request to change the current file system.\r
daf70584 2275\r
51429264 2276 @param[in] CmdLine The passed in command line.\r
daf70584 2277\r
51429264 2278 @retval EFI_SUCCESS The operation was successful.\r
daf70584
JC
2279**/\r
2280EFI_STATUS\r
47d20b54
MK
2281ChangeMappedDrive (\r
2282 IN CONST CHAR16 *CmdLine\r
daf70584
JC
2283 )\r
2284{\r
47d20b54
MK
2285 EFI_STATUS Status;\r
2286\r
daf70584
JC
2287 Status = EFI_SUCCESS;\r
2288\r
2289 //\r
2290 // make sure we are the right operation\r
2291 //\r
47d20b54 2292 ASSERT (CmdLine[(StrLen (CmdLine)-1)] == L':' && StrStr (CmdLine, L" ") == NULL);\r
ba0014b9 2293\r
daf70584
JC
2294 //\r
2295 // Call the protocol API to do the work\r
2296 //\r
47d20b54 2297 Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir (NULL, CmdLine);\r
daf70584
JC
2298\r
2299 //\r
2300 // Report any errors\r
2301 //\r
47d20b54
MK
2302 if (EFI_ERROR (Status)) {\r
2303 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);\r
daf70584
JC
2304 }\r
2305\r
2306 return (Status);\r
2307}\r
2308\r
5a5eb806
JC
2309/**\r
2310 Reprocess the command line to direct all -? to the help command.\r
2311\r
2312 if found, will add "help" as argv[0], and move the rest later.\r
2313\r
806c49db 2314 @param[in,out] CmdLine pointer to the command line to update\r
5a5eb806
JC
2315**/\r
2316EFI_STATUS\r
47d20b54
MK
2317DoHelpUpdate (\r
2318 IN OUT CHAR16 **CmdLine\r
5a5eb806
JC
2319 )\r
2320{\r
47d20b54
MK
2321 CHAR16 *CurrentParameter;\r
2322 CHAR16 *Walker;\r
2323 CHAR16 *NewCommandLine;\r
2324 EFI_STATUS Status;\r
2325 UINTN NewCmdLineSize;\r
806c49db
JC
2326\r
2327 Status = EFI_SUCCESS;\r
2328\r
47d20b54 2329 CurrentParameter = AllocateZeroPool (StrSize (*CmdLine));\r
806c49db
JC
2330 if (CurrentParameter == NULL) {\r
2331 return (EFI_OUT_OF_RESOURCES);\r
2332 }\r
2333\r
2334 Walker = *CmdLine;\r
47d20b54
MK
2335 while (Walker != NULL && *Walker != CHAR_NULL) {\r
2336 if (!EFI_ERROR (GetNextParameter (&Walker, &CurrentParameter, StrSize (*CmdLine), TRUE))) {\r
2337 if (StrStr (CurrentParameter, L"-?") == CurrentParameter) {\r
a8f98806
JC
2338 CurrentParameter[0] = L' ';\r
2339 CurrentParameter[1] = L' ';\r
47d20b54
MK
2340 NewCmdLineSize = StrSize (L"help ") + StrSize (*CmdLine);\r
2341 NewCommandLine = AllocateZeroPool (NewCmdLineSize);\r
14030c5c
JC
2342 if (NewCommandLine == NULL) {\r
2343 Status = EFI_OUT_OF_RESOURCES;\r
2344 break;\r
2345 }\r
2346\r
2347 //\r
2348 // We know the space is sufficient since we just calculated it.\r
2349 //\r
47d20b54
MK
2350 StrnCpyS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), L"help ", 5);\r
2351 StrnCatS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), *CmdLine, StrLen (*CmdLine));\r
2352 SHELL_FREE_NON_NULL (*CmdLine);\r
14030c5c 2353 *CmdLine = NewCommandLine;\r
806c49db
JC
2354 break;\r
2355 }\r
806c49db
JC
2356 }\r
2357 }\r
2358\r
47d20b54 2359 SHELL_FREE_NON_NULL (CurrentParameter);\r
806c49db
JC
2360\r
2361 return (Status);\r
2362}\r
2363\r
2364/**\r
51429264 2365 Function to update the shell variable "lasterror".\r
806c49db 2366\r
51429264 2367 @param[in] ErrorCode the error code to put into lasterror.\r
806c49db
JC
2368**/\r
2369EFI_STATUS\r
47d20b54
MK
2370SetLastError (\r
2371 IN CONST SHELL_STATUS ErrorCode\r
806c49db
JC
2372 )\r
2373{\r
47d20b54
MK
2374 CHAR16 LeString[19];\r
2375\r
2376 if (sizeof (EFI_STATUS) == sizeof (UINT64)) {\r
2377 UnicodeSPrint (LeString, sizeof (LeString), L"0x%Lx", ErrorCode);\r
806c49db 2378 } else {\r
47d20b54 2379 UnicodeSPrint (LeString, sizeof (LeString), L"0x%x", ErrorCode);\r
806c49db 2380 }\r
47d20b54
MK
2381\r
2382 DEBUG_CODE (\r
2383 InternalEfiShellSetEnv (L"debuglasterror", LeString, TRUE);\r
2384 );\r
2385 InternalEfiShellSetEnv (L"lasterror", LeString, TRUE);\r
806c49db
JC
2386\r
2387 return (EFI_SUCCESS);\r
2388}\r
2389\r
2390/**\r
6a5033ca 2391 Converts the command line to its post-processed form. this replaces variables and alias' per UEFI Shell spec.\r
806c49db
JC
2392\r
2393 @param[in,out] CmdLine pointer to the command line to update\r
2394\r
2395 @retval EFI_SUCCESS The operation was successful\r
2396 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
d08a5464 2397 @return some other error occurred\r
806c49db
JC
2398**/\r
2399EFI_STATUS\r
47d20b54
MK
2400ProcessCommandLineToFinal (\r
2401 IN OUT CHAR16 **CmdLine\r
806c49db
JC
2402 )\r
2403{\r
47d20b54
MK
2404 EFI_STATUS Status;\r
2405\r
2406 TrimSpaces (CmdLine);\r
806c49db 2407\r
47d20b54
MK
2408 Status = ShellSubstituteAliases (CmdLine);\r
2409 if (EFI_ERROR (Status)) {\r
806c49db
JC
2410 return (Status);\r
2411 }\r
2412\r
47d20b54 2413 TrimSpaces (CmdLine);\r
806c49db 2414\r
47d20b54
MK
2415 Status = ShellSubstituteVariables (CmdLine);\r
2416 if (EFI_ERROR (Status)) {\r
806c49db
JC
2417 return (Status);\r
2418 }\r
47d20b54 2419\r
81cd2f53 2420 ASSERT (*CmdLine != NULL);\r
806c49db 2421\r
47d20b54 2422 TrimSpaces (CmdLine);\r
806c49db
JC
2423\r
2424 //\r
2425 // update for help parsing\r
2426 //\r
47d20b54 2427 if (StrStr (*CmdLine, L"?") != NULL) {\r
806c49db
JC
2428 //\r
2429 // This may do nothing if the ? does not indicate help.\r
2430 // Save all the details for in the API below.\r
2431 //\r
47d20b54 2432 Status = DoHelpUpdate (CmdLine);\r
806c49db
JC
2433 }\r
2434\r
47d20b54 2435 TrimSpaces (CmdLine);\r
806c49db
JC
2436\r
2437 return (EFI_SUCCESS);\r
2438}\r
2439\r
2440/**\r
2441 Run an internal shell command.\r
2442\r
d08a5464 2443 This API will update the shell's environment since these commands are libraries.\r
ba0014b9 2444\r
806c49db
JC
2445 @param[in] CmdLine the command line to run.\r
2446 @param[in] FirstParameter the first parameter on the command line\r
2447 @param[in] ParamProtocol the shell parameters protocol pointer\r
490ce43d 2448 @param[out] CommandStatus the status from the command line.\r
806c49db
JC
2449\r
2450 @retval EFI_SUCCESS The command was completed.\r
2451 @retval EFI_ABORTED The command's operation was aborted.\r
2452**/\r
2453EFI_STATUS\r
47d20b54 2454RunInternalCommand (\r
806c49db
JC
2455 IN CONST CHAR16 *CmdLine,\r
2456 IN CHAR16 *FirstParameter,\r
490ce43d
QS
2457 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,\r
2458 OUT EFI_STATUS *CommandStatus\r
47d20b54 2459 )\r
806c49db 2460{\r
47d20b54
MK
2461 EFI_STATUS Status;\r
2462 UINTN Argc;\r
2463 CHAR16 **Argv;\r
2464 SHELL_STATUS CommandReturnedStatus;\r
2465 BOOLEAN LastError;\r
2466 CHAR16 *Walker;\r
2467 CHAR16 *NewCmdLine;\r
4b6b543e
QS
2468\r
2469 NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);\r
2470 if (NewCmdLine == NULL) {\r
2471 return EFI_OUT_OF_RESOURCES;\r
2472 }\r
2473\r
47d20b54
MK
2474 for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL; Walker++) {\r
2475 if ((*Walker == L'^') && (*(Walker+1) == L'#')) {\r
2476 CopyMem (Walker, Walker+1, StrSize (Walker) - sizeof (Walker[0]));\r
4b6b543e
QS
2477 }\r
2478 }\r
806c49db 2479\r
5a5eb806 2480 //\r
806c49db 2481 // get the argc and argv updated for internal commands\r
5a5eb806 2482 //\r
47d20b54
MK
2483 Status = UpdateArgcArgv (ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);\r
2484 if (!EFI_ERROR (Status)) {\r
5a5eb806 2485 //\r
806c49db 2486 // Run the internal command.\r
5a5eb806 2487 //\r
47d20b54 2488 Status = ShellCommandRunCommandHandler (FirstParameter, &CommandReturnedStatus, &LastError);\r
806c49db 2489\r
47d20b54 2490 if (!EFI_ERROR (Status)) {\r
490ce43d
QS
2491 if (CommandStatus != NULL) {\r
2492 if (CommandReturnedStatus != SHELL_SUCCESS) {\r
2493 *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);\r
2494 } else {\r
2495 *CommandStatus = EFI_SUCCESS;\r
2496 }\r
2497 }\r
2498\r
5a5eb806 2499 //\r
806c49db
JC
2500 // Update last error status.\r
2501 // some commands do not update last error.\r
5a5eb806 2502 //\r
806c49db 2503 if (LastError) {\r
47d20b54 2504 SetLastError (CommandReturnedStatus);\r
5a5eb806 2505 }\r
806c49db
JC
2506\r
2507 //\r
2508 // Pass thru the exitcode from the app.\r
2509 //\r
47d20b54 2510 if (ShellCommandGetExit ()) {\r
e3eb7d82
JC
2511 //\r
2512 // An Exit was requested ("exit" command), pass its value up.\r
2513 //\r
806c49db 2514 Status = CommandReturnedStatus;\r
47d20b54 2515 } else if ((CommandReturnedStatus != SHELL_SUCCESS) && IsScriptOnlyCommand (FirstParameter)) {\r
e3eb7d82
JC
2516 //\r
2517 // Always abort when a script only command fails for any reason\r
2518 //\r
2519 Status = EFI_ABORTED;\r
47d20b54 2520 } else if ((ShellCommandGetCurrentScriptFile () != NULL) && (CommandReturnedStatus == SHELL_ABORTED)) {\r
e3eb7d82
JC
2521 //\r
2522 // Abort when in a script and a command aborted\r
2523 //\r
806c49db 2524 Status = EFI_ABORTED;\r
5a5eb806 2525 }\r
806c49db
JC
2526 }\r
2527 }\r
2528\r
2529 //\r
d08a5464 2530 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.\r
806c49db
JC
2531 // This is safe even if the update API failed. In this case, it may be a no-op.\r
2532 //\r
47d20b54 2533 RestoreArgcArgv (ParamProtocol, &Argv, &Argc);\r
806c49db 2534\r
e3eb7d82 2535 //\r
d08a5464 2536 // If a script is running and the command is not a script only command, then\r
e3eb7d82
JC
2537 // change return value to success so the script won't halt (unless aborted).\r
2538 //\r
2539 // Script only commands have to be able halt the script since the script will\r
2540 // not operate if they are failing.\r
2541 //\r
47d20b54
MK
2542 if ( (ShellCommandGetCurrentScriptFile () != NULL)\r
2543 && !IsScriptOnlyCommand (FirstParameter)\r
2544 && (Status != EFI_ABORTED)\r
2545 )\r
2546 {\r
806c49db
JC
2547 Status = EFI_SUCCESS;\r
2548 }\r
2549\r
4b6b543e 2550 FreePool (NewCmdLine);\r
806c49db
JC
2551 return (Status);\r
2552}\r
2553\r
2554/**\r
2555 Function to run the command or file.\r
2556\r
2557 @param[in] Type the type of operation being run.\r
2558 @param[in] CmdLine the command line to run.\r
2559 @param[in] FirstParameter the first parameter on the command line\r
2560 @param[in] ParamProtocol the shell parameters protocol pointer\r
490ce43d 2561 @param[out] CommandStatus the status from the command line.\r
806c49db
JC
2562\r
2563 @retval EFI_SUCCESS The command was completed.\r
2564 @retval EFI_ABORTED The command's operation was aborted.\r
2565**/\r
2566EFI_STATUS\r
47d20b54 2567RunCommandOrFile (\r
806c49db
JC
2568 IN SHELL_OPERATION_TYPES Type,\r
2569 IN CONST CHAR16 *CmdLine,\r
2570 IN CHAR16 *FirstParameter,\r
490ce43d
QS
2571 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,\r
2572 OUT EFI_STATUS *CommandStatus\r
47d20b54 2573 )\r
806c49db
JC
2574{\r
2575 EFI_STATUS Status;\r
cd39fe08 2576 EFI_STATUS StartStatus;\r
806c49db 2577 CHAR16 *CommandWithPath;\r
764c21f4 2578 CHAR16 *FullCommandWithPath;\r
806c49db 2579 EFI_DEVICE_PATH_PROTOCOL *DevPath;\r
5223c121 2580 SHELL_STATUS CalleeExitStatus;\r
806c49db 2581\r
47d20b54
MK
2582 Status = EFI_SUCCESS;\r
2583 CommandWithPath = NULL;\r
2584 DevPath = NULL;\r
2585 CalleeExitStatus = SHELL_INVALID_PARAMETER;\r
806c49db
JC
2586\r
2587 switch (Type) {\r
47d20b54
MK
2588 case Internal_Command:\r
2589 Status = RunInternalCommand (CmdLine, FirstParameter, ParamProtocol, CommandStatus);\r
806c49db 2590 break;\r
47d20b54
MK
2591 case Script_File_Name:\r
2592 case Efi_Application:\r
806c49db
JC
2593 //\r
2594 // Process a fully qualified path\r
2595 //\r
47d20b54 2596 if (StrStr (FirstParameter, L":") != NULL) {\r
806c49db 2597 ASSERT (CommandWithPath == NULL);\r
47d20b54
MK
2598 if (ShellIsFile (FirstParameter) == EFI_SUCCESS) {\r
2599 CommandWithPath = StrnCatGrow (&CommandWithPath, NULL, FirstParameter, 0);\r
806c49db
JC
2600 }\r
2601 }\r
2602\r
2603 //\r
2604 // Process a relative path and also check in the path environment variable\r
2605 //\r
2606 if (CommandWithPath == NULL) {\r
47d20b54 2607 CommandWithPath = ShellFindFilePathEx (FirstParameter, mExecutableExtensions);\r
806c49db
JC
2608 }\r
2609\r
2610 //\r
2611 // This should be impossible now.\r
2612 //\r
47d20b54 2613 ASSERT (CommandWithPath != NULL);\r
806c49db
JC
2614\r
2615 //\r
2616 // Make sure that path is not just a directory (or not found)\r
2617 //\r
47d20b54
MK
2618 if (!EFI_ERROR (ShellIsDirectory (CommandWithPath))) {\r
2619 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2620 SetLastError (SHELL_NOT_FOUND);\r
806c49db 2621 }\r
47d20b54 2622\r
806c49db 2623 switch (Type) {\r
47d20b54
MK
2624 case Script_File_Name:\r
2625 FullCommandWithPath = FullyQualifyPath (CommandWithPath);\r
764c21f4
JD
2626 if (FullCommandWithPath == NULL) {\r
2627 Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);\r
2628 } else {\r
2629 Status = RunScriptFile (FullCommandWithPath, NULL, CmdLine, ParamProtocol);\r
47d20b54 2630 FreePool (FullCommandWithPath);\r
764c21f4 2631 }\r
47d20b54 2632\r
806c49db 2633 break;\r
47d20b54 2634 case Efi_Application:\r
806c49db
JC
2635 //\r
2636 // Get the device path of the application image\r
2637 //\r
47d20b54
MK
2638 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath (CommandWithPath);\r
2639 if (DevPath == NULL) {\r
806c49db
JC
2640 Status = EFI_OUT_OF_RESOURCES;\r
2641 break;\r
2642 }\r
2643\r
2644 //\r
2645 // Execute the device path\r
2646 //\r
47d20b54
MK
2647 Status = InternalShellExecuteDevicePath (\r
2648 &gImageHandle,\r
2649 DevPath,\r
2650 CmdLine,\r
2651 NULL,\r
2652 &StartStatus\r
2653 );\r
2654\r
2655 SHELL_FREE_NON_NULL (DevPath);\r
2656\r
2657 if (EFI_ERROR (Status)) {\r
2658 CalleeExitStatus = (SHELL_STATUS)(Status & (~MAX_BIT));\r
71c49eaf 2659 } else {\r
47d20b54 2660 CalleeExitStatus = (SHELL_STATUS)StartStatus;\r
71c49eaf
SQ
2661 }\r
2662\r
490ce43d
QS
2663 if (CommandStatus != NULL) {\r
2664 *CommandStatus = CalleeExitStatus;\r
2665 }\r
2666\r
806c49db
JC
2667 //\r
2668 // Update last error status.\r
2669 //\r
5223c121 2670 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS\r
47d20b54 2671 SetLastError (CalleeExitStatus);\r
806c49db 2672 break;\r
6fa0da7d
LE
2673 default:\r
2674 //\r
2675 // Do nothing.\r
2676 //\r
2677 break;\r
5a5eb806 2678 }\r
47d20b54 2679\r
5a5eb806 2680 break;\r
6fa0da7d
LE
2681 default:\r
2682 //\r
2683 // Do nothing.\r
2684 //\r
2685 break;\r
5a5eb806 2686 }\r
806c49db 2687\r
47d20b54 2688 SHELL_FREE_NON_NULL (CommandWithPath);\r
806c49db
JC
2689\r
2690 return (Status);\r
2691}\r
2692\r
2693/**\r
2694 Function to setup StdIn, StdErr, StdOut, and then run the command or file.\r
2695\r
2696 @param[in] Type the type of operation being run.\r
2697 @param[in] CmdLine the command line to run.\r
2698 @param[in] FirstParameter the first parameter on the command line.\r
2699 @param[in] ParamProtocol the shell parameters protocol pointer\r
490ce43d 2700 @param[out] CommandStatus the status from the command line.\r
806c49db
JC
2701\r
2702 @retval EFI_SUCCESS The command was completed.\r
2703 @retval EFI_ABORTED The command's operation was aborted.\r
2704**/\r
2705EFI_STATUS\r
47d20b54 2706SetupAndRunCommandOrFile (\r
490ce43d
QS
2707 IN SHELL_OPERATION_TYPES Type,\r
2708 IN CHAR16 *CmdLine,\r
2709 IN CHAR16 *FirstParameter,\r
2710 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,\r
dcbdb8bf 2711 OUT EFI_STATUS *CommandStatus\r
47d20b54 2712 )\r
806c49db 2713{\r
47d20b54
MK
2714 EFI_STATUS Status;\r
2715 SHELL_FILE_HANDLE OriginalStdIn;\r
2716 SHELL_FILE_HANDLE OriginalStdOut;\r
2717 SHELL_FILE_HANDLE OriginalStdErr;\r
2718 SYSTEM_TABLE_INFO OriginalSystemTableInfo;\r
2719 CONST SCRIPT_FILE *ConstScriptFile;\r
806c49db
JC
2720\r
2721 //\r
2722 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII\r
2723 //\r
47d20b54 2724 Status = UpdateStdInStdOutStdErr (ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
806c49db
JC
2725\r
2726 //\r
2727 // The StdIn, StdOut, and StdErr are set up.\r
2728 // Now run the command, script, or application\r
2729 //\r
47d20b54
MK
2730 if (!EFI_ERROR (Status)) {\r
2731 TrimSpaces (&CmdLine);\r
2732 Status = RunCommandOrFile (Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);\r
806c49db
JC
2733 }\r
2734\r
2735 //\r
2736 // Now print errors\r
2737 //\r
47d20b54
MK
2738 if (EFI_ERROR (Status)) {\r
2739 ConstScriptFile = ShellCommandGetCurrentScriptFile ();\r
2740 if ((ConstScriptFile == NULL) || (ConstScriptFile->CurrentCommand == NULL)) {\r
2741 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID *)(Status));\r
063aa89b 2742 } else {\r
47d20b54 2743 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT), ShellInfoObject.HiiHandle, (VOID *)(Status), ConstScriptFile->CurrentCommand->Line);\r
063aa89b 2744 }\r
806c49db
JC
2745 }\r
2746\r
2747 //\r
2748 // put back the original StdIn, StdOut, and StdErr\r
2749 //\r
47d20b54 2750 RestoreStdInStdOutStdErr (ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
806c49db
JC
2751\r
2752 return (Status);\r
5a5eb806
JC
2753}\r
2754\r
a405b86d 2755/**\r
2756 Function will process and run a command line.\r
2757\r
ba0014b9 2758 This will determine if the command line represents an internal shell\r
a405b86d 2759 command or dispatch an external application.\r
2760\r
2761 @param[in] CmdLine The command line to parse.\r
490ce43d 2762 @param[out] CommandStatus The status from the command line.\r
a405b86d 2763\r
2764 @retval EFI_SUCCESS The command was completed.\r
2765 @retval EFI_ABORTED The command's operation was aborted.\r
2766**/\r
2767EFI_STATUS\r
47d20b54
MK
2768RunShellCommand (\r
2769 IN CONST CHAR16 *CmdLine,\r
2770 OUT EFI_STATUS *CommandStatus\r
a405b86d 2771 )\r
2772{\r
47d20b54
MK
2773 EFI_STATUS Status;\r
2774 CHAR16 *CleanOriginal;\r
2775 CHAR16 *FirstParameter;\r
2776 CHAR16 *TempWalker;\r
2777 SHELL_OPERATION_TYPES Type;\r
2778 CONST CHAR16 *CurDir;\r
2779\r
2780 ASSERT (CmdLine != NULL);\r
2781 if (StrLen (CmdLine) == 0) {\r
a405b86d 2782 return (EFI_SUCCESS);\r
2783 }\r
2784\r
47d20b54
MK
2785 Status = EFI_SUCCESS;\r
2786 CleanOriginal = NULL;\r
a405b86d 2787\r
47d20b54 2788 CleanOriginal = StrnCatGrow (&CleanOriginal, NULL, CmdLine, 0);\r
532691c8 2789 if (CleanOriginal == NULL) {\r
1219c85d 2790 return (EFI_OUT_OF_RESOURCES);\r
532691c8 2791 }\r
284e034f 2792\r
47d20b54 2793 TrimSpaces (&CleanOriginal);\r
a405b86d 2794\r
284e034f 2795 //\r
d41a79a0
CP
2796 // NULL out comments (leveraged from RunScriptFileHandle() ).\r
2797 // The # character on a line is used to denote that all characters on the same line\r
2798 // and to the right of the # are to be ignored by the shell.\r
d08a5464 2799 // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.\r
d41a79a0
CP
2800 //\r
2801 for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {\r
2802 if (*TempWalker == L'^') {\r
2803 if (*(TempWalker + 1) == L'#') {\r
4b6b543e 2804 TempWalker++;\r
d41a79a0
CP
2805 }\r
2806 } else if (*TempWalker == L'#') {\r
2807 *TempWalker = CHAR_NULL;\r
2808 }\r
2809 }\r
2810\r
47d20b54 2811 TrimSpaces (&CleanOriginal);\r
d41a79a0
CP
2812\r
2813 //\r
284e034f
CP
2814 // Handle case that passed in command line is just 1 or more " " characters.\r
2815 //\r
2816 if (StrLen (CleanOriginal) == 0) {\r
47d20b54 2817 SHELL_FREE_NON_NULL (CleanOriginal);\r
1219c85d 2818 return (EFI_SUCCESS);\r
284e034f
CP
2819 }\r
2820\r
47d20b54
MK
2821 Status = ProcessCommandLineToFinal (&CleanOriginal);\r
2822 if (EFI_ERROR (Status)) {\r
2823 SHELL_FREE_NON_NULL (CleanOriginal);\r
1219c85d 2824 return (Status);\r
a405b86d 2825 }\r
2826\r
a405b86d 2827 //\r
d08a5464 2828 // We don't do normal processing with a split command line (output from one command input to another)\r
a405b86d 2829 //\r
47d20b54
MK
2830 if (ContainsSplit (CleanOriginal)) {\r
2831 Status = ProcessNewSplitCommandLine (CleanOriginal);\r
2832 SHELL_FREE_NON_NULL (CleanOriginal);\r
1219c85d 2833 return (Status);\r
ba0014b9 2834 }\r
5b5cd144 2835\r
806c49db
JC
2836 //\r
2837 // We need the first parameter information so we can determine the operation type\r
2838 //\r
47d20b54 2839 FirstParameter = AllocateZeroPool (StrSize (CleanOriginal));\r
806c49db 2840 if (FirstParameter == NULL) {\r
47d20b54 2841 SHELL_FREE_NON_NULL (CleanOriginal);\r
1219c85d 2842 return (EFI_OUT_OF_RESOURCES);\r
806c49db 2843 }\r
47d20b54 2844\r
806c49db 2845 TempWalker = CleanOriginal;\r
47d20b54 2846 if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CleanOriginal), TRUE))) {\r
14030c5c
JC
2847 //\r
2848 // Depending on the first parameter we change the behavior\r
2849 //\r
47d20b54
MK
2850 switch (Type = GetOperationType (FirstParameter)) {\r
2851 case File_Sys_Change:\r
14030c5c
JC
2852 Status = ChangeMappedDrive (FirstParameter);\r
2853 break;\r
47d20b54
MK
2854 case Internal_Command:\r
2855 case Script_File_Name:\r
2856 case Efi_Application:\r
2857 Status = SetupAndRunCommandOrFile (Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus);\r
14030c5c
JC
2858 break;\r
2859 default:\r
2860 //\r
2861 // Whatever was typed, it was invalid.\r
2862 //\r
47d20b54
MK
2863 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2864 SetLastError (SHELL_NOT_FOUND);\r
14030c5c
JC
2865 break;\r
2866 }\r
2867 } else {\r
47d20b54
MK
2868 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2869 SetLastError (SHELL_NOT_FOUND);\r
a405b86d 2870 }\r
47d20b54 2871\r
f72df356
HL
2872 //\r
2873 // Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping.\r
2874 //\r
2875 CurDir = EfiShellGetCurDir (NULL);\r
2876 if (CurDir != NULL) {\r
47d20b54 2877 if (EFI_ERROR (ShellFileExists (CurDir))) {\r
f72df356
HL
2878 //\r
2879 // EfiShellSetCurDir() cannot set current directory to NULL.\r
2880 // EfiShellSetEnv() is not allowed to set the "cwd" variable.\r
2881 // Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable.\r
2882 //\r
2883 InternalEfiShellSetEnv (L"cwd", NULL, TRUE);\r
2884 gShellCurMapping = NULL;\r
2885 }\r
2886 }\r
2887\r
47d20b54
MK
2888 SHELL_FREE_NON_NULL (CleanOriginal);\r
2889 SHELL_FREE_NON_NULL (FirstParameter);\r
a405b86d 2890\r
2891 return (Status);\r
2892}\r
2893\r
490ce43d
QS
2894/**\r
2895 Function will process and run a command line.\r
2896\r
ba0014b9 2897 This will determine if the command line represents an internal shell\r
490ce43d
QS
2898 command or dispatch an external application.\r
2899\r
2900 @param[in] CmdLine The command line to parse.\r
2901\r
2902 @retval EFI_SUCCESS The command was completed.\r
2903 @retval EFI_ABORTED The command's operation was aborted.\r
2904**/\r
2905EFI_STATUS\r
47d20b54
MK
2906RunCommand (\r
2907 IN CONST CHAR16 *CmdLine\r
490ce43d
QS
2908 )\r
2909{\r
47d20b54 2910 return (RunShellCommand (CmdLine, NULL));\r
490ce43d
QS
2911}\r
2912\r
a405b86d 2913/**\r
2914 Function to process a NSH script file via SHELL_FILE_HANDLE.\r
2915\r
2916 @param[in] Handle The handle to the already opened file.\r
2917 @param[in] Name The name of the script file.\r
2918\r
d08a5464 2919 @retval EFI_SUCCESS the script completed successfully\r
a405b86d 2920**/\r
2921EFI_STATUS\r
a405b86d 2922RunScriptFileHandle (\r
a308e058
RN
2923 IN SHELL_FILE_HANDLE Handle,\r
2924 IN CONST CHAR16 *Name\r
a405b86d 2925 )\r
2926{\r
47d20b54
MK
2927 EFI_STATUS Status;\r
2928 SCRIPT_FILE *NewScriptFile;\r
2929 UINTN LoopVar;\r
2930 UINTN PrintBuffSize;\r
2931 CHAR16 *CommandLine;\r
2932 CHAR16 *CommandLine2;\r
2933 CHAR16 *CommandLine3;\r
2934 SCRIPT_COMMAND_LIST *LastCommand;\r
2935 BOOLEAN Ascii;\r
2936 BOOLEAN PreScriptEchoState;\r
2937 BOOLEAN PreCommandEchoState;\r
2938 CONST CHAR16 *CurDir;\r
2939 UINTN LineCount;\r
2940 CHAR16 LeString[50];\r
2941 LIST_ENTRY OldBufferList;\r
2942\r
2943 ASSERT (!ShellCommandGetScriptExit ());\r
2944\r
2945 PreScriptEchoState = ShellCommandGetEchoState ();\r
2946 PrintBuffSize = PcdGet16 (PcdShellPrintBufferSize);\r
2947\r
2948 NewScriptFile = (SCRIPT_FILE *)AllocateZeroPool (sizeof (SCRIPT_FILE));\r
3c865f20 2949 if (NewScriptFile == NULL) {\r
2950 return (EFI_OUT_OF_RESOURCES);\r
2951 }\r
a405b86d 2952\r
2953 //\r
2954 // Set up the name\r
2955 //\r
47d20b54
MK
2956 ASSERT (NewScriptFile->ScriptName == NULL);\r
2957 NewScriptFile->ScriptName = StrnCatGrow (&NewScriptFile->ScriptName, NULL, Name, 0);\r
3c865f20 2958 if (NewScriptFile->ScriptName == NULL) {\r
47d20b54 2959 DeleteScriptFileStruct (NewScriptFile);\r
3c865f20 2960 return (EFI_OUT_OF_RESOURCES);\r
2961 }\r
a405b86d 2962\r
2963 //\r
2964 // Save the parameters (used to replace %0 to %9 later on)\r
2965 //\r
2966 NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;\r
2967 if (NewScriptFile->Argc != 0) {\r
47d20b54 2968 NewScriptFile->Argv = (CHAR16 **)AllocateZeroPool (NewScriptFile->Argc * sizeof (CHAR16 *));\r
3c865f20 2969 if (NewScriptFile->Argv == NULL) {\r
47d20b54 2970 DeleteScriptFileStruct (NewScriptFile);\r
3c865f20 2971 return (EFI_OUT_OF_RESOURCES);\r
2972 }\r
47d20b54 2973\r
764c21f4
JD
2974 //\r
2975 // Put the full path of the script file into Argv[0] as required by section\r
2976 // 3.6.2 of version 2.2 of the shell specification.\r
2977 //\r
47d20b54
MK
2978 NewScriptFile->Argv[0] = StrnCatGrow (&NewScriptFile->Argv[0], NULL, NewScriptFile->ScriptName, 0);\r
2979 for (LoopVar = 1; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {\r
2980 ASSERT (NewScriptFile->Argv[LoopVar] == NULL);\r
2981 NewScriptFile->Argv[LoopVar] = StrnCatGrow (&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);\r
3c865f20 2982 if (NewScriptFile->Argv[LoopVar] == NULL) {\r
47d20b54 2983 DeleteScriptFileStruct (NewScriptFile);\r
3c865f20 2984 return (EFI_OUT_OF_RESOURCES);\r
2985 }\r
a405b86d 2986 }\r
2987 } else {\r
2988 NewScriptFile->Argv = NULL;\r
2989 }\r
2990\r
47d20b54
MK
2991 InitializeListHead (&NewScriptFile->CommandList);\r
2992 InitializeListHead (&NewScriptFile->SubstList);\r
a405b86d 2993\r
2994 //\r
2995 // Now build the list of all script commands.\r
2996 //\r
2997 LineCount = 0;\r
47d20b54
MK
2998 while (!ShellFileHandleEof (Handle)) {\r
2999 CommandLine = ShellFileHandleReturnLine (Handle, &Ascii);\r
a405b86d 3000 LineCount++;\r
47d20b54
MK
3001 if ((CommandLine == NULL) || (StrLen (CommandLine) == 0) || (CommandLine[0] == '#')) {\r
3002 SHELL_FREE_NON_NULL (CommandLine);\r
a405b86d 3003 continue;\r
3004 }\r
47d20b54
MK
3005\r
3006 NewScriptFile->CurrentCommand = AllocateZeroPool (sizeof (SCRIPT_COMMAND_LIST));\r
3c865f20 3007 if (NewScriptFile->CurrentCommand == NULL) {\r
47d20b54
MK
3008 SHELL_FREE_NON_NULL (CommandLine);\r
3009 DeleteScriptFileStruct (NewScriptFile);\r
3c865f20 3010 return (EFI_OUT_OF_RESOURCES);\r
3011 }\r
a405b86d 3012\r
3013 NewScriptFile->CurrentCommand->Cl = CommandLine;\r
3014 NewScriptFile->CurrentCommand->Data = NULL;\r
3015 NewScriptFile->CurrentCommand->Line = LineCount;\r
3016\r
47d20b54 3017 InsertTailList (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
a405b86d 3018 }\r
3019\r
3020 //\r
3021 // Add this as the topmost script file\r
3022 //\r
3023 ShellCommandSetNewScript (NewScriptFile);\r
3024\r
3025 //\r
3026 // Now enumerate through the commands and run each one.\r
3027 //\r
47d20b54 3028 CommandLine = AllocateZeroPool (PrintBuffSize);\r
3c865f20 3029 if (CommandLine == NULL) {\r
47d20b54 3030 DeleteScriptFileStruct (NewScriptFile);\r
3c865f20 3031 return (EFI_OUT_OF_RESOURCES);\r
3032 }\r
47d20b54
MK
3033\r
3034 CommandLine2 = AllocateZeroPool (PrintBuffSize);\r
3c865f20 3035 if (CommandLine2 == NULL) {\r
47d20b54
MK
3036 FreePool (CommandLine);\r
3037 DeleteScriptFileStruct (NewScriptFile);\r
3c865f20 3038 return (EFI_OUT_OF_RESOURCES);\r
3039 }\r
a405b86d 3040\r
47d20b54
MK
3041 for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode (&NewScriptFile->CommandList)\r
3042 ; !IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)\r
3043 ; // conditional increment in the body of the loop\r
3044 )\r
3045 {\r
3046 ASSERT (CommandLine2 != NULL);\r
3047 StrnCpyS (\r
3048 CommandLine2,\r
3049 PrintBuffSize/sizeof (CHAR16),\r
3050 NewScriptFile->CurrentCommand->Cl,\r
3051 PrintBuffSize/sizeof (CHAR16) - 1\r
3052 );\r
a405b86d 3053\r
47d20b54 3054 SaveBufferList (&OldBufferList);\r
14810d6b 3055\r
a405b86d 3056 //\r
3057 // NULL out comments\r
3058 //\r
47d20b54 3059 for (CommandLine3 = CommandLine2; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL; CommandLine3++) {\r
a405b86d 3060 if (*CommandLine3 == L'^') {\r
4b6b543e 3061 if ( *(CommandLine3+1) == L':') {\r
47d20b54 3062 CopyMem (CommandLine3, CommandLine3+1, StrSize (CommandLine3) - sizeof (CommandLine3[0]));\r
4b6b543e
QS
3063 } else if (*(CommandLine3+1) == L'#') {\r
3064 CommandLine3++;\r
a405b86d 3065 }\r
3066 } else if (*CommandLine3 == L'#') {\r
3067 *CommandLine3 = CHAR_NULL;\r
3068 }\r
3069 }\r
3070\r
47d20b54 3071 if ((CommandLine2 != NULL) && (StrLen (CommandLine2) >= 1)) {\r
a405b86d 3072 //\r
3073 // Due to variability in starting the find and replace action we need to have both buffers the same.\r
3074 //\r
47d20b54
MK
3075 StrnCpyS (\r
3076 CommandLine,\r
3077 PrintBuffSize/sizeof (CHAR16),\r
3078 CommandLine2,\r
3079 PrintBuffSize/sizeof (CHAR16) - 1\r
3080 );\r
a405b86d 3081\r
3082 //\r
3083 // Remove the %0 to %9 from the command line (if we have some arguments)\r
3084 //\r
3085 if (NewScriptFile->Argv != NULL) {\r
3086 switch (NewScriptFile->Argc) {\r
3087 default:\r
47d20b54
MK
3088 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);\r
3089 ASSERT_EFI_ERROR (Status);\r
a405b86d 3090 case 9:\r
47d20b54
MK
3091 Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);\r
3092 ASSERT_EFI_ERROR (Status);\r
a405b86d 3093 case 8:\r
47d20b54
MK
3094 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);\r
3095 ASSERT_EFI_ERROR (Status);\r
a405b86d 3096 case 7:\r
47d20b54
MK
3097 Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);\r
3098 ASSERT_EFI_ERROR (Status);\r
a405b86d 3099 case 6:\r
47d20b54
MK
3100 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);\r
3101 ASSERT_EFI_ERROR (Status);\r
a405b86d 3102 case 5:\r
47d20b54
MK
3103 Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);\r
3104 ASSERT_EFI_ERROR (Status);\r
a405b86d 3105 case 4:\r
47d20b54
MK
3106 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);\r
3107 ASSERT_EFI_ERROR (Status);\r
a405b86d 3108 case 3:\r
47d20b54
MK
3109 Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);\r
3110 ASSERT_EFI_ERROR (Status);\r
a405b86d 3111 case 2:\r
47d20b54
MK
3112 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);\r
3113 ASSERT_EFI_ERROR (Status);\r
a405b86d 3114 case 1:\r
47d20b54
MK
3115 Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);\r
3116 ASSERT_EFI_ERROR (Status);\r
a405b86d 3117 break;\r
3118 case 0:\r
3119 break;\r
3120 }\r
3121 }\r
47d20b54
MK
3122\r
3123 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);\r
3124 Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);\r
3125 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);\r
3126 Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);\r
3127 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);\r
3128 Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);\r
3129 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);\r
3130 Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);\r
3131 Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);\r
3132\r
3133 StrnCpyS (\r
3134 CommandLine2,\r
3135 PrintBuffSize/sizeof (CHAR16),\r
3136 CommandLine,\r
3137 PrintBuffSize/sizeof (CHAR16) - 1\r
3138 );\r
a405b86d 3139\r
3140 LastCommand = NewScriptFile->CurrentCommand;\r
3141\r
47d20b54
MK
3142 for (CommandLine3 = CommandLine2; CommandLine3[0] == L' '; CommandLine3++) {\r
3143 }\r
a405b86d 3144\r
47d20b54 3145 if ((CommandLine3 != NULL) && (CommandLine3[0] == L':')) {\r
a405b86d 3146 //\r
3147 // This line is a goto target / label\r
3148 //\r
3149 } else {\r
47d20b54 3150 if ((CommandLine3 != NULL) && (StrLen (CommandLine3) > 0)) {\r
848997b5 3151 if (CommandLine3[0] == L'@') {\r
3152 //\r
3153 // We need to save the current echo state\r
3154 // and disable echo for just this command.\r
3155 //\r
47d20b54
MK
3156 PreCommandEchoState = ShellCommandGetEchoState ();\r
3157 ShellCommandSetEchoState (FALSE);\r
3158 Status = RunCommand (CommandLine3+1);\r
848997b5 3159\r
3160 //\r
284e034f 3161 // If command was "@echo -off" or "@echo -on" then don't restore echo state\r
848997b5 3162 //\r
47d20b54
MK
3163 if ((StrCmp (L"@echo -off", CommandLine3) != 0) &&\r
3164 (StrCmp (L"@echo -on", CommandLine3) != 0))\r
3165 {\r
284e034f
CP
3166 //\r
3167 // Now restore the pre-'@' echo state.\r
3168 //\r
47d20b54 3169 ShellCommandSetEchoState (PreCommandEchoState);\r
284e034f 3170 }\r
848997b5 3171 } else {\r
47d20b54
MK
3172 if (ShellCommandGetEchoState ()) {\r
3173 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv (L"cwd");\r
3174 if ((CurDir != NULL) && (StrLen (CurDir) > 1)) {\r
3175 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
0d11446d 3176 } else {\r
47d20b54 3177 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
0d11446d 3178 }\r
47d20b54
MK
3179\r
3180 ShellPrintEx (-1, -1, L"%s\r\n", CommandLine2);\r
0d11446d 3181 }\r
47d20b54
MK
3182\r
3183 Status = RunCommand (CommandLine3);\r
848997b5 3184 }\r
a405b86d 3185 }\r
3186\r
47d20b54 3187 if (ShellCommandGetScriptExit ()) {\r
5b5cd144
CP
3188 //\r
3189 // ShellCommandGetExitCode() always returns a UINT64\r
3190 //\r
47d20b54
MK
3191 UnicodeSPrint (LeString, sizeof (LeString), L"0x%Lx", ShellCommandGetExitCode ());\r
3192 DEBUG_CODE (\r
3193 InternalEfiShellSetEnv (L"debuglasterror", LeString, TRUE);\r
3194 );\r
3195 InternalEfiShellSetEnv (L"lasterror", LeString, TRUE);\r
b6b22b13 3196\r
47d20b54 3197 ShellCommandRegisterExit (FALSE, 0);\r
a405b86d 3198 Status = EFI_SUCCESS;\r
47d20b54 3199 RestoreBufferList (&OldBufferList);\r
a405b86d 3200 break;\r
3201 }\r
47d20b54
MK
3202\r
3203 if (ShellGetExecutionBreakFlag ()) {\r
3204 RestoreBufferList (&OldBufferList);\r
c6a7fef8
ED
3205 break;\r
3206 }\r
47d20b54
MK
3207\r
3208 if (EFI_ERROR (Status)) {\r
3209 RestoreBufferList (&OldBufferList);\r
a405b86d 3210 break;\r
3211 }\r
47d20b54
MK
3212\r
3213 if (ShellCommandGetExit ()) {\r
3214 RestoreBufferList (&OldBufferList);\r
a405b86d 3215 break;\r
3216 }\r
3217 }\r
47d20b54 3218\r
a405b86d 3219 //\r
3220 // If that commend did not update the CurrentCommand then we need to advance it...\r
3221 //\r
3222 if (LastCommand == NewScriptFile->CurrentCommand) {\r
47d20b54
MK
3223 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
3224 if (!IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
a405b86d 3225 NewScriptFile->CurrentCommand->Reset = TRUE;\r
3226 }\r
3227 }\r
3228 } else {\r
47d20b54
MK
3229 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
3230 if (!IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
a405b86d 3231 NewScriptFile->CurrentCommand->Reset = TRUE;\r
3232 }\r
3233 }\r
a405b86d 3234\r
47d20b54
MK
3235 RestoreBufferList (&OldBufferList);\r
3236 }\r
a405b86d 3237\r
47d20b54
MK
3238 FreePool (CommandLine);\r
3239 FreePool (CommandLine2);\r
a405b86d 3240 ShellCommandSetNewScript (NULL);\r
733f138d 3241\r
3242 //\r
3243 // Only if this was the last script reset the state.\r
3244 //\r
47d20b54
MK
3245 if (ShellCommandGetCurrentScriptFile () == NULL) {\r
3246 ShellCommandSetEchoState (PreScriptEchoState);\r
733f138d 3247 }\r
47d20b54 3248\r
a405b86d 3249 return (EFI_SUCCESS);\r
3250}\r
3251\r
3252/**\r
3253 Function to process a NSH script file.\r
3254\r
3255 @param[in] ScriptPath Pointer to the script file name (including file system path).\r
e958b946
JC
3256 @param[in] Handle the handle of the script file already opened.\r
3257 @param[in] CmdLine the command line to run.\r
3258 @param[in] ParamProtocol the shell parameters protocol pointer\r
a405b86d 3259\r
d08a5464 3260 @retval EFI_SUCCESS the script completed successfully\r
a405b86d 3261**/\r
3262EFI_STATUS\r
a405b86d 3263RunScriptFile (\r
a308e058
RN
3264 IN CONST CHAR16 *ScriptPath,\r
3265 IN SHELL_FILE_HANDLE Handle OPTIONAL,\r
3266 IN CONST CHAR16 *CmdLine,\r
3267 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol\r
a405b86d 3268 )\r
3269{\r
47d20b54
MK
3270 EFI_STATUS Status;\r
3271 SHELL_FILE_HANDLE FileHandle;\r
3272 UINTN Argc;\r
3273 CHAR16 **Argv;\r
a405b86d 3274\r
47d20b54 3275 if (ShellIsFile (ScriptPath) != EFI_SUCCESS) {\r
a405b86d 3276 return (EFI_INVALID_PARAMETER);\r
3277 }\r
3278\r
e958b946
JC
3279 //\r
3280 // get the argc and argv updated for scripts\r
3281 //\r
47d20b54
MK
3282 Status = UpdateArgcArgv (ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc);\r
3283 if (!EFI_ERROR (Status)) {\r
e958b946
JC
3284 if (Handle == NULL) {\r
3285 //\r
3286 // open the file\r
3287 //\r
47d20b54
MK
3288 Status = ShellOpenFileByName (ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
3289 if (!EFI_ERROR (Status)) {\r
e958b946
JC
3290 //\r
3291 // run it\r
3292 //\r
47d20b54 3293 Status = RunScriptFileHandle (FileHandle, ScriptPath);\r
a405b86d 3294\r
e958b946
JC
3295 //\r
3296 // now close the file\r
3297 //\r
47d20b54 3298 ShellCloseFile (&FileHandle);\r
e958b946
JC
3299 }\r
3300 } else {\r
47d20b54 3301 Status = RunScriptFileHandle (Handle, ScriptPath);\r
e958b946
JC
3302 }\r
3303 }\r
3304\r
3305 //\r
d08a5464 3306 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.\r
e958b946
JC
3307 // This is safe even if the update API failed. In this case, it may be a no-op.\r
3308 //\r
47d20b54 3309 RestoreArgcArgv (ParamProtocol, &Argv, &Argc);\r
a405b86d 3310\r
3311 return (Status);\r
3312}\r
00534bc3 3313\r
1219c85d
RN
3314/**\r
3315 Return the pointer to the first occurrence of any character from a list of characters.\r
3316\r
3317 @param[in] String the string to parse\r
3318 @param[in] CharacterList the list of character to look for\r
3319 @param[in] EscapeCharacter An escape character to skip\r
3320\r
3321 @return the location of the first character in the string\r
3322 @retval CHAR_NULL no instance of any character in CharacterList was found in String\r
3323**/\r
47d20b54
MK
3324CONST CHAR16 *\r
3325FindFirstCharacter (\r
3326 IN CONST CHAR16 *String,\r
3327 IN CONST CHAR16 *CharacterList,\r
3328 IN CONST CHAR16 EscapeCharacter\r
1219c85d
RN
3329 )\r
3330{\r
47d20b54
MK
3331 UINT32 WalkChar;\r
3332 UINT32 WalkStr;\r
1219c85d 3333\r
47d20b54 3334 for (WalkStr = 0; WalkStr < StrLen (String); WalkStr++) {\r
1219c85d
RN
3335 if (String[WalkStr] == EscapeCharacter) {\r
3336 WalkStr++;\r
3337 continue;\r
3338 }\r
47d20b54
MK
3339\r
3340 for (WalkChar = 0; WalkChar < StrLen (CharacterList); WalkChar++) {\r
1219c85d
RN
3341 if (String[WalkStr] == CharacterList[WalkChar]) {\r
3342 return (&String[WalkStr]);\r
3343 }\r
3344 }\r
3345 }\r
47d20b54
MK
3346\r
3347 return (String + StrLen (String));\r
1219c85d 3348}\r