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