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