]> git.proxmox.com Git - mirror_edk2.git/blame - ShellPkg/Application/Shell/Shell.c
ArmPkg/PlatformBootManagerLib: implement new generic version
[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
907 &gEfiUnicodeCollationProtocolGuid,\r
908 NULL,\r
909 (VOID **) &UnicodeCollation\r
910 );\r
911 if (EFI_ERROR (Status)) {\r
912 return Status;\r
913 }\r
914\r
915 // Set default options\r
916 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = FALSE;\r
917 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = FALSE;\r
918 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;\r
919 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = FALSE;\r
920 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = FALSE;\r
921 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = FALSE;\r
922 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = FALSE;\r
923 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = FALSE;\r
924 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = FALSE;\r
dcbdb8bf 925 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = FALSE;\r
23385d63
BJ
926 ShellInfoObject.ShellInitSettings.Delay = 5;\r
927\r
e9d19a80
CP
928 //\r
929 // Start LoopVar at 0 to parse only optional arguments at Argv[0]\r
930 // and parse other parameters from Argv[1]. This is for use case that\r
931 // UEFI Shell boot option is created, and OptionalData is provided\r
932 // that starts with shell command-line options.\r
933 //\r
934 for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
23385d63
BJ
935 CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];\r
936 if (UnicodeCollation->StriColl (\r
937 UnicodeCollation,\r
938 L"-startup",\r
939 CurrentArg\r
940 ) == 0) {\r
941 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE;\r
3c865f20 942 }\r
23385d63
BJ
943 else if (UnicodeCollation->StriColl (\r
944 UnicodeCollation,\r
945 L"-nostartup",\r
946 CurrentArg\r
947 ) == 0) {\r
948 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = TRUE;\r
949 }\r
950 else if (UnicodeCollation->StriColl (\r
951 UnicodeCollation,\r
952 L"-noconsoleout",\r
953 CurrentArg\r
954 ) == 0) {\r
955 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;\r
956 }\r
957 else if (UnicodeCollation->StriColl (\r
958 UnicodeCollation,\r
959 L"-noconsolein",\r
960 CurrentArg\r
961 ) == 0) {\r
962 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = TRUE;\r
963 }\r
964 else if (UnicodeCollation->StriColl (\r
965 UnicodeCollation,\r
966 L"-nointerrupt",\r
967 CurrentArg\r
968 ) == 0) {\r
969 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = TRUE;\r
970 }\r
971 else if (UnicodeCollation->StriColl (\r
972 UnicodeCollation,\r
973 L"-nomap",\r
974 CurrentArg\r
975 ) == 0) {\r
976 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = TRUE;\r
977 }\r
978 else if (UnicodeCollation->StriColl (\r
979 UnicodeCollation,\r
980 L"-noversion",\r
981 CurrentArg\r
982 ) == 0) {\r
983 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = TRUE;\r
984 }\r
dcbdb8bf
QS
985 else if (UnicodeCollation->StriColl (\r
986 UnicodeCollation,\r
987 L"-nonest",\r
988 CurrentArg\r
989 ) == 0) {\r
990 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = TRUE;\r
991 }\r
23385d63
BJ
992 else if (UnicodeCollation->StriColl (\r
993 UnicodeCollation,\r
994 L"-delay",\r
995 CurrentArg\r
996 ) == 0) {\r
997 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE;\r
998 // Check for optional delay value following "-delay"\r
999 DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];\r
1000 if (DelayValueStr != NULL){\r
1001 if (*DelayValueStr == L':') {\r
1002 DelayValueStr++;\r
1003 }\r
1004 if (!EFI_ERROR(ShellConvertStringToUint64 (\r
1005 DelayValueStr,\r
1006 &DelayValue,\r
1007 FALSE,\r
1008 FALSE\r
1009 ))) {\r
1010 ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;\r
1011 LoopVar++;\r
1012 }\r
1013 }\r
1014 } else if (UnicodeCollation->StriColl (\r
1015 UnicodeCollation,\r
1016 L"-_exit",\r
1017 CurrentArg\r
1018 ) == 0) {\r
1019 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = TRUE;\r
1020 } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {\r
d08a5464 1021 // Unrecognized option\r
23385d63
BJ
1022 ShellPrintHiiEx(-1, -1, NULL,\r
1023 STRING_TOKEN (STR_GEN_PROBLEM),\r
1024 ShellInfoObject.HiiHandle,\r
1025 CurrentArg\r
1026 );\r
1027 return EFI_INVALID_PARAMETER;\r
1028 } else {\r
e9d19a80
CP
1029 //\r
1030 // First argument should be Shell.efi image name\r
1031 //\r
1032 if (LoopVar == 0) {\r
1033 continue;\r
1034 }\r
1035\r
7f79b01e 1036 ShellInfoObject.ShellInitSettings.FileName = AllocateCopyPool(StrSize(CurrentArg), CurrentArg);\r
23385d63
BJ
1037 if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
1038 return (EFI_OUT_OF_RESOURCES);\r
1039 }\r
1040 //\r
1041 // We found `file-name`.\r
1042 //\r
1043 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
23385d63
BJ
1044 LoopVar++;\r
1045\r
1046 // Add `file-name-options`\r
ba71f790 1047 for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
23385d63
BJ
1048 ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
1049 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
1050 &Size,\r
1051 L" ",\r
1052 0);\r
1053 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
1054 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
1055 return (EFI_OUT_OF_RESOURCES);\r
1056 }\r
1057 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
1058 &Size,\r
1059 gEfiShellParametersProtocol->Argv[LoopVar],\r
1060 0);\r
1061 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
1062 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
1063 return (EFI_OUT_OF_RESOURCES);\r
a405b86d 1064 }\r
1065 }\r
a405b86d 1066 }\r
1067 }\r
1068\r
23385d63 1069 // "-nointerrupt" overrides "-delay"\r
733f138d 1070 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
1071 ShellInfoObject.ShellInitSettings.Delay = 0;\r
a405b86d 1072 }\r
a405b86d 1073\r
23385d63 1074 return EFI_SUCCESS;\r
a405b86d 1075}\r
1076\r
1077/**\r
1078 Handles all interaction with the default startup script.\r
1079\r
1080 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.\r
1081\r
1082 @param ImagePath the path to the image for shell. first place to look for the startup script\r
1083 @param FilePath the path to the file for shell. second place to look for the startup script.\r
1084\r
1085 @retval EFI_SUCCESS the variable is initialized.\r
1086**/\r
1087EFI_STATUS\r
1088EFIAPI\r
1089DoStartupScript(\r
a308e058
RN
1090 IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,\r
1091 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
a405b86d 1092 )\r
1093{\r
1094 EFI_STATUS Status;\r
490ce43d 1095 EFI_STATUS CalleeStatus;\r
a405b86d 1096 UINTN Delay;\r
1097 EFI_INPUT_KEY Key;\r
1098 SHELL_FILE_HANDLE FileHandle;\r
1099 EFI_DEVICE_PATH_PROTOCOL *NewPath;\r
1100 EFI_DEVICE_PATH_PROTOCOL *NamePath;\r
1101 CHAR16 *FileStringPath;\r
733f138d 1102 CHAR16 *TempSpot;\r
a405b86d 1103 UINTN NewSize;\r
733f138d 1104 CONST CHAR16 *MapName;\r
a405b86d 1105\r
1106 Key.UnicodeChar = CHAR_NULL;\r
1107 Key.ScanCode = 0;\r
1108 FileHandle = NULL;\r
1109\r
1110 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
1111 //\r
1112 // launch something else instead\r
1113 //\r
1114 NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);\r
1115 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
1116 NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);\r
1117 }\r
1118 FileStringPath = AllocateZeroPool(NewSize);\r
3c865f20 1119 if (FileStringPath == NULL) {\r
1120 return (EFI_OUT_OF_RESOURCES);\r
1121 }\r
e75390f0 1122 StrCpyS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileName);\r
a405b86d 1123 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
e75390f0
QS
1124 StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), L" ", NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);\r
1125 StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);\r
a405b86d 1126 }\r
490ce43d
QS
1127 Status = RunShellCommand(FileStringPath, &CalleeStatus);\r
1128 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {\r
01e3a976 1129 ShellCommandRegisterExit(gEfiShellProtocol->BatchIsActive(), (UINT64)CalleeStatus);\r
490ce43d 1130 }\r
a405b86d 1131 FreePool(FileStringPath);\r
1132 return (Status);\r
1133\r
1134 }\r
1135\r
1136 //\r
1137 // for shell level 0 we do no scripts\r
1138 // Without the Startup bit overriding we allow for nostartup to prevent scripts\r
1139 //\r
1140 if ( (PcdGet8(PcdShellSupportLevel) < 1)\r
1141 || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)\r
1142 ){\r
1143 return (EFI_SUCCESS);\r
1144 }\r
1145\r
5559a41a 1146 gST->ConOut->EnableCursor(gST->ConOut, FALSE);\r
a405b86d 1147 //\r
1148 // print out our warning and see if they press a key\r
1149 //\r
284e034f 1150 for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay\r
733f138d 1151 ; Delay != 0 && EFI_ERROR(Status)\r
a405b86d 1152 ; Delay--\r
1153 ){\r
284e034f
CP
1154 ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);\r
1155 gBS->Stall (1000000);\r
733f138d 1156 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
1157 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
1158 }\r
a405b86d 1159 }\r
1160 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);\r
5559a41a 1161 gST->ConOut->EnableCursor(gST->ConOut, TRUE);\r
a405b86d 1162\r
1163 //\r
1164 // ESC was pressed\r
1165 //\r
1166 if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {\r
1167 return (EFI_SUCCESS);\r
1168 }\r
1169\r
a405b86d 1170 //\r
733f138d 1171 // Try the first location (must be file system)\r
a405b86d 1172 //\r
733f138d 1173 MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);\r
1174 if (MapName != NULL) {\r
1175 FileStringPath = NULL;\r
1176 NewSize = 0;\r
1177 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);\r
532691c8 1178 if (FileStringPath == NULL) {\r
1179 Status = EFI_OUT_OF_RESOURCES;\r
1180 } else {\r
1181 TempSpot = StrStr(FileStringPath, L";");\r
1182 if (TempSpot != NULL) {\r
1183 *TempSpot = CHAR_NULL;\r
1184 }\r
1185 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);\r
1186 PathRemoveLastItem(FileStringPath);\r
1187 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);\r
1188 Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);\r
1189 FreePool(FileStringPath);\r
733f138d 1190 }\r
733f138d 1191 }\r
a405b86d 1192 if (EFI_ERROR(Status)) {\r
733f138d 1193 NamePath = FileDevicePath (NULL, mStartupScript);\r
1194 NewPath = AppendDevicePathNode (ImagePath, NamePath);\r
1195 FreePool(NamePath);\r
1196\r
a405b86d 1197 //\r
733f138d 1198 // Try the location\r
a405b86d 1199 //\r
a405b86d 1200 Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
733f138d 1201 FreePool(NewPath);\r
a405b86d 1202 }\r
a405b86d 1203 //\r
1204 // If we got a file, run it\r
1205 //\r
733f138d 1206 if (!EFI_ERROR(Status) && FileHandle != NULL) {\r
a308e058 1207 Status = RunScriptFile (mStartupScript, FileHandle, L"", ShellInfoObject.NewShellParametersProtocol);\r
a405b86d 1208 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);\r
1209 } else {\r
3c865f20 1210 FileStringPath = ShellFindFilePath(mStartupScript);\r
1211 if (FileStringPath == NULL) {\r
1212 //\r
d08a5464 1213 // we return success since we don't need to have a startup script\r
3c865f20 1214 //\r
1215 Status = EFI_SUCCESS;\r
1216 ASSERT(FileHandle == NULL);\r
1217 } else {\r
a308e058 1218 Status = RunScriptFile(FileStringPath, NULL, L"", ShellInfoObject.NewShellParametersProtocol);\r
3c865f20 1219 FreePool(FileStringPath);\r
1220 }\r
a405b86d 1221 }\r
1222\r
a405b86d 1223\r
1224 return (Status);\r
1225}\r
1226\r
1227/**\r
1228 Function to perform the shell prompt looping. It will do a single prompt,\r
1229 dispatch the result, and then return. It is expected that the caller will\r
1230 call this function in a loop many times.\r
1231\r
1232 @retval EFI_SUCCESS\r
1233 @retval RETURN_ABORTED\r
1234**/\r
1235EFI_STATUS\r
1236EFIAPI\r
1237DoShellPrompt (\r
1238 VOID\r
1239 )\r
1240{\r
1241 UINTN Column;\r
1242 UINTN Row;\r
1243 CHAR16 *CmdLine;\r
1244 CONST CHAR16 *CurDir;\r
1245 UINTN BufferSize;\r
1246 EFI_STATUS Status;\r
594e780b 1247 LIST_ENTRY OldBufferList;\r
a405b86d 1248\r
1249 CurDir = NULL;\r
1250\r
1251 //\r
1252 // Get screen setting to decide size of the command line buffer\r
1253 //\r
1254 gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);\r
1255 BufferSize = Column * Row * sizeof (CHAR16);\r
1256 CmdLine = AllocateZeroPool (BufferSize);\r
1257 if (CmdLine == NULL) {\r
1258 return EFI_OUT_OF_RESOURCES;\r
1259 }\r
1260\r
594e780b 1261 SaveBufferList(&OldBufferList);\r
a405b86d 1262 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
1263\r
1264 //\r
1265 // Prompt for input\r
1266 //\r
1267 gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);\r
1268\r
1269 if (CurDir != NULL && StrLen(CurDir) > 1) {\r
1270 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
1271 } else {\r
1272 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
1273 }\r
1274\r
1275 //\r
1276 // Read a line from the console\r
1277 //\r
1278 Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);\r
1279\r
1280 //\r
1281 // Null terminate the string and parse it\r
1282 //\r
1283 if (!EFI_ERROR (Status)) {\r
1284 CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;\r
a308e058 1285 Status = RunCommand(CmdLine);\r
4922715d 1286 }\r
a405b86d 1287\r
1288 //\r
1289 // Done with this command\r
1290 //\r
594e780b 1291 RestoreBufferList(&OldBufferList);\r
a405b86d 1292 FreePool (CmdLine);\r
1293 return Status;\r
1294}\r
1295\r
1296/**\r
1297 Add a buffer to the Buffer To Free List for safely returning buffers to other\r
1298 places without risking letting them modify internal shell information.\r
1299\r
1300 @param Buffer Something to pass to FreePool when the shell is exiting.\r
1301**/\r
1302VOID*\r
1303EFIAPI\r
1304AddBufferToFreeList(\r
1305 VOID *Buffer\r
1306 )\r
1307{\r
1308 BUFFER_LIST *BufferListEntry;\r
1309\r
1310 if (Buffer == NULL) {\r
1311 return (NULL);\r
1312 }\r
1313\r
1314 BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));\r
1315 ASSERT(BufferListEntry != NULL);\r
1316 BufferListEntry->Buffer = Buffer;\r
1317 InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);\r
1318 return (Buffer);\r
1319}\r
1320\r
14810d6b
QS
1321\r
1322/**\r
1323 Create a new buffer list and stores the old one to OldBufferList \r
1324\r
1325 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.\r
1326**/\r
1327VOID\r
1328SaveBufferList (\r
1329 OUT LIST_ENTRY *OldBufferList\r
1330 )\r
1331{\r
1332 CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));\r
1333 InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);\r
1334}\r
1335\r
1336/**\r
1337 Restore previous nodes into BufferToFreeList .\r
1338\r
1339 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.\r
1340**/\r
1341VOID\r
1342RestoreBufferList (\r
1343 IN OUT LIST_ENTRY *OldBufferList\r
1344 )\r
1345{\r
1346 FreeBufferList (&ShellInfoObject.BufferToFreeList);\r
1347 CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));\r
1348}\r
1349\r
1350\r
a405b86d 1351/**\r
1352 Add a buffer to the Line History List\r
1353\r
1354 @param Buffer The line buffer to add.\r
1355**/\r
1356VOID\r
1357EFIAPI\r
1358AddLineToCommandHistory(\r
1359 IN CONST CHAR16 *Buffer\r
1360 )\r
1361{\r
1362 BUFFER_LIST *Node;\r
a4f138a4
QS
1363 BUFFER_LIST *Walker;\r
1364 UINT16 MaxHistoryCmdCount;\r
1365 UINT16 Count;\r
dcbdb8bf 1366\r
a4f138a4
QS
1367 Count = 0;\r
1368 MaxHistoryCmdCount = PcdGet16(PcdShellMaxHistoryCommandCount);\r
1369 \r
1370 if (MaxHistoryCmdCount == 0) {\r
1371 return ;\r
1372 }\r
1373\r
a405b86d 1374\r
1375 Node = AllocateZeroPool(sizeof(BUFFER_LIST));\r
1376 ASSERT(Node != NULL);\r
7f79b01e 1377 Node->Buffer = AllocateCopyPool(StrSize(Buffer), Buffer);\r
a405b86d 1378 ASSERT(Node->Buffer != NULL);\r
a405b86d 1379\r
a4f138a4
QS
1380 for ( Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link)\r
1381 ; !IsNull(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)\r
1382 ; Walker = (BUFFER_LIST*)GetNextNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)\r
1383 ){\r
1384 Count++;\r
1385 }\r
1386 if (Count < MaxHistoryCmdCount){\r
1387 InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
1388 } else {\r
1389 Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link);\r
1390 RemoveEntryList(&Walker->Link);\r
1391 if (Walker->Buffer != NULL) {\r
1392 FreePool(Walker->Buffer);\r
1393 }\r
1394 FreePool(Walker);\r
1395 InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
1396 }\r
a405b86d 1397}\r
1398\r
1399/**\r
1400 Checks if a string is an alias for another command. If yes, then it replaces the alias name\r
1401 with the correct command name.\r
1402\r
4ff7e37b
ED
1403 @param[in, out] CommandString Upon entry the potential alias. Upon return the\r
1404 command name if it was an alias. If it was not\r
1405 an alias it will be unchanged. This function may\r
1406 change the buffer to fit the command name.\r
a405b86d 1407\r
1408 @retval EFI_SUCCESS The name was changed.\r
1409 @retval EFI_SUCCESS The name was not an alias.\r
1410 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
1411**/\r
1412EFI_STATUS\r
1413EFIAPI\r
1414ShellConvertAlias(\r
1415 IN OUT CHAR16 **CommandString\r
1416 )\r
1417{\r
1418 CONST CHAR16 *NewString;\r
1419\r
1420 NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);\r
1421 if (NewString == NULL) {\r
1422 return (EFI_SUCCESS);\r
1423 }\r
1424 FreePool(*CommandString);\r
7f79b01e 1425 *CommandString = AllocateCopyPool(StrSize(NewString), NewString);\r
a405b86d 1426 if (*CommandString == NULL) {\r
1427 return (EFI_OUT_OF_RESOURCES);\r
1428 }\r
a405b86d 1429 return (EFI_SUCCESS);\r
1430}\r
1431\r
d5b5440b
JC
1432/**\r
1433 This function will eliminate unreplaced (and therefore non-found) environment variables.\r
1434\r
1435 @param[in,out] CmdLine The command line to update.\r
1436**/\r
1437EFI_STATUS\r
1438EFIAPI\r
1439StripUnreplacedEnvironmentVariables(\r
1440 IN OUT CHAR16 *CmdLine\r
1441 )\r
1442{\r
1443 CHAR16 *FirstPercent;\r
1444 CHAR16 *FirstQuote;\r
1445 CHAR16 *SecondPercent;\r
1446 CHAR16 *SecondQuote;\r
1447 CHAR16 *CurrentLocator;\r
1448\r
1449 for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) {\r
a95cf8f0
JC
1450 FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE);\r
1451 FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE);\r
1452 SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL;\r
d5b5440b
JC
1453 if (FirstPercent == NULL || SecondPercent == NULL) {\r
1454 //\r
d08a5464 1455 // If we ever don't have 2 % we are done.\r
d5b5440b
JC
1456 //\r
1457 break;\r
1458 }\r
1459\r
fdd52bde 1460 if (FirstQuote!= NULL && FirstQuote < FirstPercent) {\r
a95cf8f0 1461 SecondQuote = FindNextInstance(FirstQuote+1, L"\"", TRUE);\r
d5b5440b
JC
1462 //\r
1463 // Quote is first found\r
1464 //\r
a95cf8f0 1465\r
d5b5440b
JC
1466 if (SecondQuote < FirstPercent) {\r
1467 //\r
1468 // restart after the pair of "\r
1469 //\r
1470 CurrentLocator = SecondQuote + 1;\r
1471 } else /* FirstPercent < SecondQuote */{\r
1472 //\r
1473 // Restart on the first percent\r
1474 //\r
1475 CurrentLocator = FirstPercent;\r
1476 }\r
1477 continue;\r
1478 }\r
fdd52bde 1479 \r
fe8ec3dd 1480 if (FirstQuote == NULL || SecondPercent < FirstQuote) {\r
42435671
QS
1481 if (IsValidEnvironmentVariableName(FirstPercent, SecondPercent)) {\r
1482 //\r
1483 // We need to remove from FirstPercent to SecondPercent\r
1484 //\r
1485 CopyMem(FirstPercent, SecondPercent + 1, StrSize(SecondPercent + 1));\r
1486 //\r
d08a5464 1487 // don't need to update the locator. both % characters are gone.\r
42435671
QS
1488 //\r
1489 } else {\r
1490 CurrentLocator = SecondPercent + 1;\r
1491 }\r
d5b5440b
JC
1492 continue;\r
1493 }\r
d5b5440b
JC
1494 CurrentLocator = FirstQuote;\r
1495 }\r
1496 return (EFI_SUCCESS);\r
1497}\r
1498\r
a405b86d 1499/**\r
1500 Function allocates a new command line and replaces all instances of environment\r
1501 variable names that are correctly preset to their values.\r
1502\r
1503 If the return value is not NULL the memory must be caller freed.\r
1504\r
1505 @param[in] OriginalCommandLine The original command line\r
1506\r
d08a5464 1507 @retval NULL An error occurred.\r
a405b86d 1508 @return The new command line with no environment variables present.\r
1509**/\r
1510CHAR16*\r
1511EFIAPI\r
1512ShellConvertVariables (\r
1513 IN CONST CHAR16 *OriginalCommandLine\r
1514 )\r
1515{\r
1516 CONST CHAR16 *MasterEnvList;\r
1517 UINTN NewSize;\r
1518 CHAR16 *NewCommandLine1;\r
1519 CHAR16 *NewCommandLine2;\r
1520 CHAR16 *Temp;\r
1521 UINTN ItemSize;\r
1522 CHAR16 *ItemTemp;\r
1523 SCRIPT_FILE *CurrentScriptFile;\r
1524 ALIAS_LIST *AliasListNode;\r
1525\r
1526 ASSERT(OriginalCommandLine != NULL);\r
1527\r
1528 ItemSize = 0;\r
1529 NewSize = StrSize(OriginalCommandLine);\r
1530 CurrentScriptFile = ShellCommandGetCurrentScriptFile();\r
1531 Temp = NULL;\r
1532\r
1533 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?\r
1534\r
1535 //\r
1536 // calculate the size required for the post-conversion string...\r
1537 //\r
1538 if (CurrentScriptFile != NULL) {\r
1539 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1540 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1541 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1542 ){\r
1543 for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)\r
1544 ; Temp != NULL\r
1545 ; Temp = StrStr(Temp+1, AliasListNode->Alias)\r
1546 ){\r
1547 //\r
d08a5464 1548 // we need a preceding and if there is space no ^ preceding (if no space ignore)\r
a405b86d 1549 //\r
1550 if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {\r
1551 NewSize += StrSize(AliasListNode->CommandString);\r
1552 }\r
1553 }\r
1554 }\r
1555 }\r
1556\r
1557 for (MasterEnvList = EfiShellGetEnv(NULL)\r
1558 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL\r
1559 ; MasterEnvList += StrLen(MasterEnvList) + 1\r
1560 ){\r
1561 if (StrSize(MasterEnvList) > ItemSize) {\r
1562 ItemSize = StrSize(MasterEnvList);\r
1563 }\r
1564 for (Temp = StrStr(OriginalCommandLine, MasterEnvList)\r
1565 ; Temp != NULL\r
1566 ; Temp = StrStr(Temp+1, MasterEnvList)\r
1567 ){\r
1568 //\r
d08a5464 1569 // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)\r
a405b86d 1570 //\r
1571 if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&\r
1572 ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {\r
1573 NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));\r
1574 }\r
1575 }\r
1576 }\r
1577\r
a405b86d 1578 //\r
1579 // now do the replacements...\r
1580 //\r
7f79b01e 1581 NewCommandLine1 = AllocateCopyPool(NewSize, OriginalCommandLine);\r
a405b86d 1582 NewCommandLine2 = AllocateZeroPool(NewSize);\r
1583 ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));\r
3c865f20 1584 if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {\r
1585 SHELL_FREE_NON_NULL(NewCommandLine1);\r
1586 SHELL_FREE_NON_NULL(NewCommandLine2);\r
1587 SHELL_FREE_NON_NULL(ItemTemp);\r
1588 return (NULL);\r
1589 }\r
a405b86d 1590 for (MasterEnvList = EfiShellGetEnv(NULL)\r
7f79b01e 1591 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL\r
a405b86d 1592 ; MasterEnvList += StrLen(MasterEnvList) + 1\r
1593 ){\r
e75390f0
QS
1594 StrCpyS( ItemTemp, \r
1595 ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)), \r
1596 L"%"\r
1597 );\r
1598 StrCatS( ItemTemp, \r
1599 ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)), \r
1600 MasterEnvList\r
1601 );\r
1602 StrCatS( ItemTemp, \r
1603 ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)), \r
1604 L"%"\r
1605 );\r
a405b86d 1606 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);\r
e75390f0 1607 StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);\r
a405b86d 1608 }\r
1609 if (CurrentScriptFile != NULL) {\r
1610 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1611 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1612 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1613 ){\r
1614 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);\r
e75390f0 1615 StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);\r
a405b86d 1616 }\r
1617 }\r
1618\r
fdd52bde 1619 //\r
d08a5464 1620 // Remove non-existent environment variables\r
fdd52bde
JC
1621 //\r
1622 StripUnreplacedEnvironmentVariables(NewCommandLine1);\r
1623\r
df07baea
JC
1624 //\r
1625 // Now cleanup any straggler intentionally ignored "%" characters\r
1626 //\r
1627 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);\r
e75390f0 1628 StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);\r
df07baea 1629 \r
a405b86d 1630 FreePool(NewCommandLine2);\r
1631 FreePool(ItemTemp);\r
1632\r
1633 return (NewCommandLine1);\r
1634}\r
1635\r
1636/**\r
1637 Internal function to run a command line with pipe usage.\r
1638\r
1639 @param[in] CmdLine The pointer to the command line.\r
1640 @param[in] StdIn The pointer to the Standard input.\r
1641 @param[in] StdOut The pointer to the Standard output.\r
1642\r
1643 @retval EFI_SUCCESS The split command is executed successfully.\r
1644 @retval other Some error occurs when executing the split command.\r
1645**/\r
1646EFI_STATUS\r
1647EFIAPI\r
1648RunSplitCommand(\r
1649 IN CONST CHAR16 *CmdLine,\r
1650 IN SHELL_FILE_HANDLE *StdIn,\r
a308e058 1651 IN SHELL_FILE_HANDLE *StdOut\r
a405b86d 1652 )\r
1653{\r
1654 EFI_STATUS Status;\r
1655 CHAR16 *NextCommandLine;\r
1656 CHAR16 *OurCommandLine;\r
1657 UINTN Size1;\r
1658 UINTN Size2;\r
1659 SPLIT_LIST *Split;\r
1660 SHELL_FILE_HANDLE *TempFileHandle;\r
1661 BOOLEAN Unicode;\r
1662\r
1663 ASSERT(StdOut == NULL);\r
1664\r
1665 ASSERT(StrStr(CmdLine, L"|") != NULL);\r
1666\r
1667 Status = EFI_SUCCESS;\r
1668 NextCommandLine = NULL;\r
1669 OurCommandLine = NULL;\r
1670 Size1 = 0;\r
1671 Size2 = 0;\r
1672\r
1673 NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);\r
1674 OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine);\r
532691c8 1675\r
1676 if (NextCommandLine == NULL || OurCommandLine == NULL) {\r
1677 SHELL_FREE_NON_NULL(OurCommandLine);\r
1678 SHELL_FREE_NON_NULL(NextCommandLine);\r
1679 return (EFI_OUT_OF_RESOURCES);\r
19216573 1680 } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {\r
1681 SHELL_FREE_NON_NULL(OurCommandLine);\r
1682 SHELL_FREE_NON_NULL(NextCommandLine);\r
1683 return (EFI_INVALID_PARAMETER);\r
a5382ce0
QS
1684 } else if (NextCommandLine[0] == L'a' &&\r
1685 (NextCommandLine[1] == L' ' || NextCommandLine[1] == CHAR_NULL)\r
1686 ){\r
a405b86d 1687 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
a5382ce0
QS
1688 while (NextCommandLine[0] == L' ') {\r
1689 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
1690 }\r
1691 if (NextCommandLine[0] == CHAR_NULL) {\r
1692 SHELL_FREE_NON_NULL(OurCommandLine);\r
1693 SHELL_FREE_NON_NULL(NextCommandLine);\r
1694 return (EFI_INVALID_PARAMETER);\r
1695 }\r
a405b86d 1696 Unicode = FALSE;\r
1697 } else {\r
1698 Unicode = TRUE;\r
1699 }\r
1700\r
1701\r
1702 //\r
1703 // make a SPLIT_LIST item and add to list\r
1704 //\r
1705 Split = AllocateZeroPool(sizeof(SPLIT_LIST));\r
1706 ASSERT(Split != NULL);\r
1707 Split->SplitStdIn = StdIn;\r
1708 Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);\r
1709 ASSERT(Split->SplitStdOut != NULL);\r
1710 InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
1711\r
a308e058 1712 Status = RunCommand(OurCommandLine);\r
a405b86d 1713\r
1714 //\r
1715 // move the output from the first to the in to the second.\r
1716 //\r
1717 TempFileHandle = Split->SplitStdOut;\r
1718 if (Split->SplitStdIn == StdIn) {\r
1719 Split->SplitStdOut = NULL;\r
1720 } else {\r
1721 Split->SplitStdOut = Split->SplitStdIn;\r
1722 }\r
1723 Split->SplitStdIn = TempFileHandle;\r
1724 ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);\r
1725\r
1726 if (!EFI_ERROR(Status)) {\r
a308e058 1727 Status = RunCommand(NextCommandLine);\r
a405b86d 1728 }\r
1729\r
1730 //\r
1731 // remove the top level from the ScriptList\r
1732 //\r
1733 ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);\r
1734 RemoveEntryList(&Split->Link);\r
1735\r
1736 //\r
1737 // Note that the original StdIn is now the StdOut...\r
1738 //\r
1739 if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {\r
1740 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));\r
1741 }\r
1742 if (Split->SplitStdIn != NULL) {\r
1743 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));\r
1744 }\r
1745\r
1746 FreePool(Split);\r
1747 FreePool(NextCommandLine);\r
1748 FreePool(OurCommandLine);\r
1749\r
1750 return (Status);\r
1751}\r
1752\r
1ef61d03
JC
1753/**\r
1754 Take the original command line, substitute any variables, free \r
51429264 1755 the original string, return the modified copy.\r
1ef61d03 1756\r
51429264 1757 @param[in] CmdLine pointer to the command line to update.\r
1ef61d03 1758\r
51429264
SQ
1759 @retval EFI_SUCCESS the function was successful.\r
1760 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.\r
1ef61d03
JC
1761**/\r
1762EFI_STATUS\r
1763EFIAPI\r
1764ShellSubstituteVariables(\r
1765 IN CHAR16 **CmdLine\r
1766 )\r
1767{\r
1768 CHAR16 *NewCmdLine;\r
1769 NewCmdLine = ShellConvertVariables(*CmdLine);\r
1770 SHELL_FREE_NON_NULL(*CmdLine);\r
1771 if (NewCmdLine == NULL) {\r
1772 return (EFI_OUT_OF_RESOURCES);\r
1773 }\r
1774 *CmdLine = NewCmdLine;\r
1775 return (EFI_SUCCESS);\r
1776}\r
1777\r
ca53c0af
JC
1778/**\r
1779 Take the original command line, substitute any alias in the first group of space delimited characters, free \r
51429264 1780 the original string, return the modified copy.\r
ca53c0af 1781\r
51429264 1782 @param[in] CmdLine pointer to the command line to update.\r
ca53c0af 1783\r
51429264
SQ
1784 @retval EFI_SUCCESS the function was successful.\r
1785 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.\r
ca53c0af
JC
1786**/\r
1787EFI_STATUS\r
1788EFIAPI\r
1789ShellSubstituteAliases(\r
1790 IN CHAR16 **CmdLine\r
1791 )\r
1792{\r
1793 CHAR16 *NewCmdLine;\r
1794 CHAR16 *CommandName;\r
1795 EFI_STATUS Status;\r
1796 UINTN PostAliasSize;\r
1797 ASSERT(CmdLine != NULL);\r
1798 ASSERT(*CmdLine!= NULL);\r
1799\r
1800\r
1801 CommandName = NULL;\r
1802 if (StrStr((*CmdLine), L" ") == NULL){\r
1803 StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);\r
1804 } else {\r
1805 StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));\r
1806 }\r
1807\r
1808 //\r
1809 // This cannot happen 'inline' since the CmdLine can need extra space.\r
1810 //\r
1811 NewCmdLine = NULL;\r
1812 if (!ShellCommandIsCommandOnList(CommandName)) {\r
1813 //\r
1814 // Convert via alias\r
1815 //\r
1816 Status = ShellConvertAlias(&CommandName);\r
1817 if (EFI_ERROR(Status)){\r
1818 return (Status);\r
1819 }\r
1820 PostAliasSize = 0;\r
1821 NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);\r
1822 if (NewCmdLine == NULL) {\r
1823 SHELL_FREE_NON_NULL(CommandName);\r
1824 SHELL_FREE_NON_NULL(*CmdLine);\r
1825 return (EFI_OUT_OF_RESOURCES);\r
1826 }\r
1827 NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);\r
1828 if (NewCmdLine == NULL) {\r
1829 SHELL_FREE_NON_NULL(CommandName);\r
1830 SHELL_FREE_NON_NULL(*CmdLine);\r
1831 return (EFI_OUT_OF_RESOURCES);\r
1832 }\r
1833 } else {\r
1834 NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);\r
1835 }\r
1836\r
1837 SHELL_FREE_NON_NULL(*CmdLine);\r
1838 SHELL_FREE_NON_NULL(CommandName);\r
1839 \r
1840 //\r
1841 // re-assign the passed in double pointer to point to our newly allocated buffer\r
1842 //\r
1843 *CmdLine = NewCmdLine;\r
1844\r
1845 return (EFI_SUCCESS);\r
1846}\r
1847\r
6ba2921d
JC
1848/**\r
1849 Takes the Argv[0] part of the command line and determine the meaning of it.\r
e7831c90 1850\r
51429264 1851 @param[in] CmdName pointer to the command line to update.\r
e7831c90 1852 \r
51429264
SQ
1853 @retval Internal_Command The name is an internal command.\r
1854 @retval File_Sys_Change the name is a file system change.\r
1855 @retval Script_File_Name the name is a NSH script file.\r
1856 @retval Unknown_Invalid the name is unknown.\r
1857 @retval Efi_Application the name is an application (.EFI).\r
6ba2921d
JC
1858**/\r
1859SHELL_OPERATION_TYPES\r
1860EFIAPI\r
1861GetOperationType(\r
1862 IN CONST CHAR16 *CmdName\r
1863 )\r
1864{\r
1865 CHAR16* FileWithPath;\r
1866 CONST CHAR16* TempLocation;\r
1867 CONST CHAR16* TempLocation2;\r
1868\r
1869 FileWithPath = NULL;\r
1870 //\r
1871 // test for an internal command.\r
1872 //\r
1873 if (ShellCommandIsCommandOnList(CmdName)) {\r
51429264 1874 return (Internal_Command);\r
6ba2921d
JC
1875 }\r
1876\r
1877 //\r
fc4c7b30 1878 // Test for file system change request. anything ending with first : and cant have spaces.\r
6ba2921d
JC
1879 //\r
1880 if (CmdName[(StrLen(CmdName)-1)] == L':') {\r
fc4c7b30
JC
1881 if ( StrStr(CmdName, L" ") != NULL \r
1882 || StrLen(StrStr(CmdName, L":")) > 1\r
1883 ) {\r
51429264 1884 return (Unknown_Invalid);\r
6ba2921d 1885 }\r
51429264 1886 return (File_Sys_Change);\r
6ba2921d
JC
1887 }\r
1888\r
1889 //\r
1890 // Test for a file\r
1891 //\r
1892 if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {\r
1893 //\r
1894 // See if that file has a script file extension\r
1895 //\r
1896 if (StrLen(FileWithPath) > 4) {\r
1897 TempLocation = FileWithPath+StrLen(FileWithPath)-4;\r
1898 TempLocation2 = mScriptExtension;\r
1899 if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {\r
1900 SHELL_FREE_NON_NULL(FileWithPath);\r
51429264 1901 return (Script_File_Name);\r
6ba2921d
JC
1902 }\r
1903 }\r
1904\r
1905 //\r
1906 // Was a file, but not a script. we treat this as an application.\r
1907 //\r
1908 SHELL_FREE_NON_NULL(FileWithPath);\r
51429264 1909 return (Efi_Application);\r
6ba2921d
JC
1910 }\r
1911 \r
1912 SHELL_FREE_NON_NULL(FileWithPath);\r
1913 //\r
1914 // No clue what this is... return invalid flag...\r
1915 //\r
51429264 1916 return (Unknown_Invalid);\r
6ba2921d
JC
1917}\r
1918\r
de4ebdcf
SQ
1919/**\r
1920 Determine if the first item in a command line is valid.\r
1921\r
1922 @param[in] CmdLine The command line to parse.\r
1923\r
1924 @retval EFI_SUCCESS The item is valid.\r
1925 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
1926 @retval EFI_NOT_FOUND The operation type is unknown or invalid.\r
1927**/\r
6bd64463
JC
1928EFI_STATUS \r
1929EFIAPI\r
1930IsValidSplit(\r
1931 IN CONST CHAR16 *CmdLine\r
1932 )\r
1933{\r
1934 CHAR16 *Temp;\r
1935 CHAR16 *FirstParameter;\r
1936 CHAR16 *TempWalker;\r
1937 EFI_STATUS Status;\r
1938\r
1939 Temp = NULL;\r
1940\r
1941 Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);\r
1942 if (Temp == NULL) {\r
1943 return (EFI_OUT_OF_RESOURCES);\r
1944 }\r
1945\r
1946 FirstParameter = StrStr(Temp, L"|");\r
1947 if (FirstParameter != NULL) {\r
1948 *FirstParameter = CHAR_NULL;\r
1949 }\r
1950\r
1951 FirstParameter = NULL;\r
1952\r
1953 //\r
1954 // Process the command line\r
1955 //\r
1956 Status = ProcessCommandLineToFinal(&Temp);\r
1957\r
1958 if (!EFI_ERROR(Status)) {\r
1959 FirstParameter = AllocateZeroPool(StrSize(CmdLine));\r
1960 if (FirstParameter == NULL) {\r
1961 SHELL_FREE_NON_NULL(Temp);\r
1962 return (EFI_OUT_OF_RESOURCES);\r
1963 }\r
1964 TempWalker = (CHAR16*)Temp;\r
d1c275c6 1965 if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CmdLine), TRUE))) {\r
14030c5c
JC
1966 if (GetOperationType(FirstParameter) == Unknown_Invalid) {\r
1967 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
1968 SetLastError(SHELL_NOT_FOUND);\r
1969 Status = EFI_NOT_FOUND;\r
1970 }\r
6bd64463
JC
1971 }\r
1972 }\r
1973\r
1974 SHELL_FREE_NON_NULL(Temp);\r
1975 SHELL_FREE_NON_NULL(FirstParameter);\r
1976 return Status;\r
1977}\r
1978\r
1979/**\r
51429264 1980 Determine if a command line contains with a split contains only valid commands.\r
6bd64463
JC
1981\r
1982 @param[in] CmdLine The command line to parse.\r
1983\r
1984 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.\r
1985 @retval EFI_ABORTED CmdLine has at least one invalid command or application.\r
1986**/\r
1987EFI_STATUS\r
1988EFIAPI\r
1989VerifySplit(\r
1990 IN CONST CHAR16 *CmdLine\r
1991 )\r
1992{\r
1993 CONST CHAR16 *TempSpot;\r
1994 EFI_STATUS Status;\r
1995\r
1996 //\r
a5382ce0 1997 // If this was the only item, then get out\r
6bd64463 1998 //\r
a5382ce0
QS
1999 if (!ContainsSplit(CmdLine)) {\r
2000 return (EFI_SUCCESS);\r
6bd64463
JC
2001 }\r
2002\r
2003 //\r
a5382ce0 2004 // Verify up to the pipe or end character\r
6bd64463 2005 //\r
a5382ce0
QS
2006 Status = IsValidSplit(CmdLine);\r
2007 if (EFI_ERROR(Status)) {\r
2008 return (Status);\r
6bd64463
JC
2009 }\r
2010\r
2011 //\r
2012 // recurse to verify the next item\r
2013 //\r
00534bc3 2014 TempSpot = FindFirstCharacter(CmdLine, L"|", L'^') + 1;\r
a5382ce0
QS
2015 if (*TempSpot == L'a' && \r
2016 (*(TempSpot + 1) == L' ' || *(TempSpot + 1) == CHAR_NULL)\r
2017 ) {\r
2018 // If it's an ASCII pipe '|a'\r
2019 TempSpot += 1;\r
2020 }\r
2021 \r
6bd64463
JC
2022 return (VerifySplit(TempSpot));\r
2023}\r
2024\r
680db511 2025/**\r
e7831c90
JC
2026 Process a split based operation.\r
2027\r
a308e058 2028 @param[in] CmdLine pointer to the command line to process\r
e7831c90
JC
2029\r
2030 @retval EFI_SUCCESS The operation was successful\r
d08a5464 2031 @return an error occurred.\r
680db511
JC
2032**/\r
2033EFI_STATUS\r
2034EFIAPI\r
2035ProcessNewSplitCommandLine(\r
a308e058 2036 IN CONST CHAR16 *CmdLine\r
680db511
JC
2037 )\r
2038{\r
2039 SPLIT_LIST *Split;\r
2040 EFI_STATUS Status;\r
2041\r
6bd64463
JC
2042 Status = VerifySplit(CmdLine);\r
2043 if (EFI_ERROR(Status)) {\r
2044 return (Status);\r
2045 }\r
2046\r
680db511
JC
2047 Split = NULL;\r
2048\r
2049 //\r
2050 // are we in an existing split???\r
2051 //\r
2052 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
2053 Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
2054 }\r
2055\r
2056 if (Split == NULL) {\r
a308e058 2057 Status = RunSplitCommand(CmdLine, NULL, NULL);\r
680db511 2058 } else {\r
a308e058 2059 Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
680db511
JC
2060 }\r
2061 if (EFI_ERROR(Status)) {\r
2062 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);\r
2063 }\r
2064 return (Status);\r
2065}\r
daf70584
JC
2066\r
2067/**\r
51429264 2068 Handle a request to change the current file system.\r
daf70584 2069\r
51429264 2070 @param[in] CmdLine The passed in command line.\r
daf70584 2071\r
51429264 2072 @retval EFI_SUCCESS The operation was successful.\r
daf70584
JC
2073**/\r
2074EFI_STATUS\r
2075EFIAPI\r
2076ChangeMappedDrive(\r
2077 IN CONST CHAR16 *CmdLine\r
2078 )\r
2079{\r
2080 EFI_STATUS Status;\r
2081 Status = EFI_SUCCESS;\r
2082\r
2083 //\r
2084 // make sure we are the right operation\r
2085 //\r
2086 ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);\r
2087 \r
2088 //\r
2089 // Call the protocol API to do the work\r
2090 //\r
2091 Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);\r
2092\r
2093 //\r
2094 // Report any errors\r
2095 //\r
2096 if (EFI_ERROR(Status)) {\r
2097 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);\r
2098 }\r
2099\r
2100 return (Status);\r
2101}\r
2102\r
5a5eb806
JC
2103/**\r
2104 Reprocess the command line to direct all -? to the help command.\r
2105\r
2106 if found, will add "help" as argv[0], and move the rest later.\r
2107\r
806c49db 2108 @param[in,out] CmdLine pointer to the command line to update\r
5a5eb806
JC
2109**/\r
2110EFI_STATUS\r
2111EFIAPI\r
806c49db
JC
2112DoHelpUpdate(\r
2113 IN OUT CHAR16 **CmdLine\r
5a5eb806
JC
2114 )\r
2115{\r
806c49db
JC
2116 CHAR16 *CurrentParameter;\r
2117 CHAR16 *Walker;\r
806c49db
JC
2118 CHAR16 *NewCommandLine;\r
2119 EFI_STATUS Status;\r
e75390f0 2120 UINTN NewCmdLineSize;\r
806c49db
JC
2121\r
2122 Status = EFI_SUCCESS;\r
2123\r
2124 CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));\r
2125 if (CurrentParameter == NULL) {\r
2126 return (EFI_OUT_OF_RESOURCES);\r
2127 }\r
2128\r
2129 Walker = *CmdLine;\r
2130 while(Walker != NULL && *Walker != CHAR_NULL) {\r
d1c275c6 2131 if (!EFI_ERROR(GetNextParameter(&Walker, &CurrentParameter, StrSize(*CmdLine), TRUE))) {\r
14030c5c 2132 if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {\r
a8f98806
JC
2133 CurrentParameter[0] = L' ';\r
2134 CurrentParameter[1] = L' ';\r
e75390f0
QS
2135 NewCmdLineSize = StrSize(L"help ") + StrSize(*CmdLine);\r
2136 NewCommandLine = AllocateZeroPool(NewCmdLineSize);\r
14030c5c
JC
2137 if (NewCommandLine == NULL) {\r
2138 Status = EFI_OUT_OF_RESOURCES;\r
2139 break;\r
2140 }\r
2141\r
2142 //\r
2143 // We know the space is sufficient since we just calculated it.\r
2144 //\r
e75390f0
QS
2145 StrnCpyS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), L"help ", 5);\r
2146 StrnCatS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), *CmdLine, StrLen(*CmdLine));\r
14030c5c
JC
2147 SHELL_FREE_NON_NULL(*CmdLine);\r
2148 *CmdLine = NewCommandLine;\r
806c49db
JC
2149 break;\r
2150 }\r
806c49db
JC
2151 }\r
2152 }\r
2153\r
2154 SHELL_FREE_NON_NULL(CurrentParameter);\r
2155\r
2156 return (Status);\r
2157}\r
2158\r
2159/**\r
51429264 2160 Function to update the shell variable "lasterror".\r
806c49db 2161\r
51429264 2162 @param[in] ErrorCode the error code to put into lasterror.\r
806c49db
JC
2163**/\r
2164EFI_STATUS\r
2165EFIAPI\r
2166SetLastError(\r
6bd64463 2167 IN CONST SHELL_STATUS ErrorCode\r
806c49db
JC
2168 )\r
2169{\r
2170 CHAR16 LeString[19];\r
2171 if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
2172 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ErrorCode);\r
2173 } else {\r
2174 UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ErrorCode);\r
2175 }\r
2176 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
2177 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
2178\r
2179 return (EFI_SUCCESS);\r
2180}\r
2181\r
2182/**\r
2183 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.\r
2184\r
2185 @param[in,out] CmdLine pointer to the command line to update\r
2186\r
2187 @retval EFI_SUCCESS The operation was successful\r
2188 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
d08a5464 2189 @return some other error occurred\r
806c49db
JC
2190**/\r
2191EFI_STATUS\r
2192EFIAPI\r
12a27a6d 2193ProcessCommandLineToFinal(\r
806c49db
JC
2194 IN OUT CHAR16 **CmdLine\r
2195 )\r
2196{\r
2197 EFI_STATUS Status;\r
2198 TrimSpaces(CmdLine);\r
2199\r
2200 Status = ShellSubstituteAliases(CmdLine);\r
2201 if (EFI_ERROR(Status)) {\r
2202 return (Status);\r
2203 }\r
2204\r
2205 TrimSpaces(CmdLine);\r
2206\r
2207 Status = ShellSubstituteVariables(CmdLine);\r
2208 if (EFI_ERROR(Status)) {\r
2209 return (Status);\r
2210 }\r
81cd2f53 2211 ASSERT (*CmdLine != NULL);\r
806c49db
JC
2212\r
2213 TrimSpaces(CmdLine);\r
2214\r
2215 //\r
2216 // update for help parsing\r
2217 //\r
2218 if (StrStr(*CmdLine, L"?") != NULL) {\r
2219 //\r
2220 // This may do nothing if the ? does not indicate help.\r
2221 // Save all the details for in the API below.\r
2222 //\r
2223 Status = DoHelpUpdate(CmdLine);\r
2224 }\r
2225\r
2226 TrimSpaces(CmdLine);\r
2227\r
2228 return (EFI_SUCCESS);\r
2229}\r
2230\r
2231/**\r
2232 Run an internal shell command.\r
2233\r
d08a5464 2234 This API will update the shell's environment since these commands are libraries.\r
806c49db
JC
2235 \r
2236 @param[in] CmdLine the command line to run.\r
2237 @param[in] FirstParameter the first parameter on the command line\r
2238 @param[in] ParamProtocol the shell parameters protocol pointer\r
490ce43d 2239 @param[out] CommandStatus the status from the command line.\r
806c49db
JC
2240\r
2241 @retval EFI_SUCCESS The command was completed.\r
2242 @retval EFI_ABORTED The command's operation was aborted.\r
2243**/\r
2244EFI_STATUS\r
2245EFIAPI\r
2246RunInternalCommand(\r
2247 IN CONST CHAR16 *CmdLine,\r
2248 IN CHAR16 *FirstParameter,\r
490ce43d
QS
2249 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,\r
2250 OUT EFI_STATUS *CommandStatus\r
806c49db
JC
2251)\r
2252{\r
2253 EFI_STATUS Status;\r
2254 UINTN Argc;\r
2255 CHAR16 **Argv;\r
2256 SHELL_STATUS CommandReturnedStatus;\r
2257 BOOLEAN LastError;\r
4b6b543e
QS
2258 CHAR16 *Walker;\r
2259 CHAR16 *NewCmdLine; \r
2260\r
2261 NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);\r
2262 if (NewCmdLine == NULL) {\r
2263 return EFI_OUT_OF_RESOURCES;\r
2264 }\r
2265\r
2266 for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {\r
2267 if (*Walker == L'^' && *(Walker+1) == L'#') {\r
2268 CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));\r
2269 }\r
2270 }\r
806c49db 2271\r
5a5eb806 2272 //\r
806c49db 2273 // get the argc and argv updated for internal commands\r
5a5eb806 2274 //\r
d1c275c6 2275 Status = UpdateArgcArgv(ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);\r
806c49db 2276 if (!EFI_ERROR(Status)) {\r
5a5eb806 2277 //\r
806c49db 2278 // Run the internal command.\r
5a5eb806 2279 //\r
806c49db
JC
2280 Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);\r
2281\r
2282 if (!EFI_ERROR(Status)) {\r
490ce43d
QS
2283 if (CommandStatus != NULL) {\r
2284 if (CommandReturnedStatus != SHELL_SUCCESS) {\r
2285 *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);\r
2286 } else {\r
2287 *CommandStatus = EFI_SUCCESS;\r
2288 }\r
2289 }\r
2290\r
5a5eb806 2291 //\r
806c49db
JC
2292 // Update last error status.\r
2293 // some commands do not update last error.\r
5a5eb806 2294 //\r
806c49db
JC
2295 if (LastError) {\r
2296 SetLastError(CommandReturnedStatus);\r
5a5eb806 2297 }\r
806c49db
JC
2298\r
2299 //\r
2300 // Pass thru the exitcode from the app.\r
2301 //\r
2302 if (ShellCommandGetExit()) {\r
e3eb7d82
JC
2303 //\r
2304 // An Exit was requested ("exit" command), pass its value up.\r
2305 //\r
806c49db 2306 Status = CommandReturnedStatus;\r
e3eb7d82
JC
2307 } else if (CommandReturnedStatus != SHELL_SUCCESS && IsScriptOnlyCommand(FirstParameter)) {\r
2308 //\r
2309 // Always abort when a script only command fails for any reason\r
2310 //\r
2311 Status = EFI_ABORTED;\r
2312 } else if (ShellCommandGetCurrentScriptFile() != NULL && CommandReturnedStatus == SHELL_ABORTED) {\r
2313 //\r
2314 // Abort when in a script and a command aborted\r
2315 //\r
806c49db 2316 Status = EFI_ABORTED;\r
5a5eb806 2317 }\r
806c49db
JC
2318 }\r
2319 }\r
2320\r
2321 //\r
d08a5464 2322 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.\r
806c49db
JC
2323 // This is safe even if the update API failed. In this case, it may be a no-op.\r
2324 //\r
2325 RestoreArgcArgv(ParamProtocol, &Argv, &Argc);\r
2326\r
e3eb7d82 2327 //\r
d08a5464 2328 // If a script is running and the command is not a script only command, then\r
e3eb7d82
JC
2329 // change return value to success so the script won't halt (unless aborted).\r
2330 //\r
2331 // Script only commands have to be able halt the script since the script will\r
2332 // not operate if they are failing.\r
2333 //\r
2334 if ( ShellCommandGetCurrentScriptFile() != NULL\r
2335 && !IsScriptOnlyCommand(FirstParameter)\r
2336 && Status != EFI_ABORTED\r
2337 ) {\r
806c49db
JC
2338 Status = EFI_SUCCESS;\r
2339 }\r
2340\r
4b6b543e 2341 FreePool (NewCmdLine);\r
806c49db
JC
2342 return (Status);\r
2343}\r
2344\r
2345/**\r
2346 Function to run the command or file.\r
2347\r
2348 @param[in] Type the type of operation being run.\r
2349 @param[in] CmdLine the command line to run.\r
2350 @param[in] FirstParameter the first parameter on the command line\r
2351 @param[in] ParamProtocol the shell parameters protocol pointer\r
490ce43d 2352 @param[out] CommandStatus the status from the command line.\r
806c49db
JC
2353\r
2354 @retval EFI_SUCCESS The command was completed.\r
2355 @retval EFI_ABORTED The command's operation was aborted.\r
2356**/\r
2357EFI_STATUS\r
2358EFIAPI\r
2359RunCommandOrFile(\r
2360 IN SHELL_OPERATION_TYPES Type,\r
2361 IN CONST CHAR16 *CmdLine,\r
2362 IN CHAR16 *FirstParameter,\r
490ce43d
QS
2363 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,\r
2364 OUT EFI_STATUS *CommandStatus\r
806c49db
JC
2365)\r
2366{\r
2367 EFI_STATUS Status;\r
cd39fe08 2368 EFI_STATUS StartStatus;\r
806c49db
JC
2369 CHAR16 *CommandWithPath;\r
2370 EFI_DEVICE_PATH_PROTOCOL *DevPath;\r
5223c121 2371 SHELL_STATUS CalleeExitStatus;\r
806c49db
JC
2372\r
2373 Status = EFI_SUCCESS;\r
2374 CommandWithPath = NULL;\r
2375 DevPath = NULL;\r
71c49eaf 2376 CalleeExitStatus = SHELL_INVALID_PARAMETER;\r
806c49db
JC
2377\r
2378 switch (Type) {\r
51429264 2379 case Internal_Command:\r
490ce43d 2380 Status = RunInternalCommand(CmdLine, FirstParameter, ParamProtocol, CommandStatus);\r
806c49db 2381 break;\r
51429264
SQ
2382 case Script_File_Name:\r
2383 case Efi_Application:\r
806c49db
JC
2384 //\r
2385 // Process a fully qualified path\r
2386 //\r
2387 if (StrStr(FirstParameter, L":") != NULL) {\r
2388 ASSERT (CommandWithPath == NULL);\r
2389 if (ShellIsFile(FirstParameter) == EFI_SUCCESS) {\r
2390 CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0);\r
2391 }\r
2392 }\r
2393\r
2394 //\r
2395 // Process a relative path and also check in the path environment variable\r
2396 //\r
2397 if (CommandWithPath == NULL) {\r
2398 CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);\r
2399 }\r
2400\r
2401 //\r
2402 // This should be impossible now.\r
2403 //\r
2404 ASSERT(CommandWithPath != NULL);\r
2405\r
2406 //\r
2407 // Make sure that path is not just a directory (or not found)\r
2408 //\r
2409 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) {\r
2410 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
6bd64463 2411 SetLastError(SHELL_NOT_FOUND);\r
806c49db
JC
2412 }\r
2413 switch (Type) {\r
51429264 2414 case Script_File_Name:\r
a308e058 2415 Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);\r
806c49db 2416 break;\r
51429264 2417 case Efi_Application:\r
806c49db
JC
2418 //\r
2419 // Get the device path of the application image\r
2420 //\r
2421 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
2422 if (DevPath == NULL){\r
2423 Status = EFI_OUT_OF_RESOURCES;\r
2424 break;\r
2425 }\r
2426\r
2427 //\r
2428 // Execute the device path\r
2429 //\r
2430 Status = InternalShellExecuteDevicePath(\r
2431 &gImageHandle,\r
2432 DevPath,\r
2433 CmdLine,\r
2434 NULL,\r
a308e058 2435 &StartStatus\r
806c49db
JC
2436 );\r
2437\r
2438 SHELL_FREE_NON_NULL(DevPath);\r
2439\r
71c49eaf
SQ
2440 if(EFI_ERROR (Status)) {\r
2441 CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));\r
2442 } else {\r
cd39fe08 2443 CalleeExitStatus = (SHELL_STATUS) StartStatus;\r
71c49eaf
SQ
2444 }\r
2445\r
490ce43d
QS
2446 if (CommandStatus != NULL) {\r
2447 *CommandStatus = CalleeExitStatus;\r
2448 }\r
2449\r
806c49db
JC
2450 //\r
2451 // Update last error status.\r
2452 //\r
5223c121 2453 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS\r
71c49eaf 2454 SetLastError(CalleeExitStatus);\r
806c49db 2455 break;\r
6fa0da7d
LE
2456 default:\r
2457 //\r
2458 // Do nothing.\r
2459 //\r
2460 break;\r
5a5eb806
JC
2461 }\r
2462 break;\r
6fa0da7d
LE
2463 default:\r
2464 //\r
2465 // Do nothing.\r
2466 //\r
2467 break;\r
5a5eb806 2468 }\r
806c49db
JC
2469\r
2470 SHELL_FREE_NON_NULL(CommandWithPath);\r
2471\r
2472 return (Status);\r
2473}\r
2474\r
2475/**\r
2476 Function to setup StdIn, StdErr, StdOut, and then run the command or file.\r
2477\r
2478 @param[in] Type the type of operation being run.\r
2479 @param[in] CmdLine the command line to run.\r
2480 @param[in] FirstParameter the first parameter on the command line.\r
2481 @param[in] ParamProtocol the shell parameters protocol pointer\r
490ce43d 2482 @param[out] CommandStatus the status from the command line.\r
806c49db
JC
2483\r
2484 @retval EFI_SUCCESS The command was completed.\r
2485 @retval EFI_ABORTED The command's operation was aborted.\r
2486**/\r
2487EFI_STATUS\r
2488EFIAPI\r
2489SetupAndRunCommandOrFile(\r
490ce43d
QS
2490 IN SHELL_OPERATION_TYPES Type,\r
2491 IN CHAR16 *CmdLine,\r
2492 IN CHAR16 *FirstParameter,\r
2493 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,\r
dcbdb8bf 2494 OUT EFI_STATUS *CommandStatus\r
806c49db
JC
2495)\r
2496{\r
2497 EFI_STATUS Status;\r
2498 SHELL_FILE_HANDLE OriginalStdIn;\r
2499 SHELL_FILE_HANDLE OriginalStdOut;\r
2500 SHELL_FILE_HANDLE OriginalStdErr;\r
2501 SYSTEM_TABLE_INFO OriginalSystemTableInfo;\r
2502\r
2503 //\r
2504 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII\r
2505 //\r
2506 Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
2507\r
2508 //\r
2509 // The StdIn, StdOut, and StdErr are set up.\r
2510 // Now run the command, script, or application\r
2511 //\r
2512 if (!EFI_ERROR(Status)) {\r
75eb337f 2513 TrimSpaces(&CmdLine);\r
490ce43d 2514 Status = RunCommandOrFile(Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);\r
806c49db
JC
2515 }\r
2516\r
2517 //\r
2518 // Now print errors\r
2519 //\r
2520 if (EFI_ERROR(Status)) {\r
2521 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
2522 }\r
2523\r
2524 //\r
2525 // put back the original StdIn, StdOut, and StdErr\r
2526 //\r
2527 RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
2528\r
2529 return (Status);\r
5a5eb806
JC
2530}\r
2531\r
a405b86d 2532/**\r
2533 Function will process and run a command line.\r
2534\r
2535 This will determine if the command line represents an internal shell \r
2536 command or dispatch an external application.\r
2537\r
2538 @param[in] CmdLine The command line to parse.\r
490ce43d 2539 @param[out] CommandStatus The status from the command line.\r
a405b86d 2540\r
2541 @retval EFI_SUCCESS The command was completed.\r
2542 @retval EFI_ABORTED The command's operation was aborted.\r
2543**/\r
2544EFI_STATUS\r
2545EFIAPI\r
490ce43d
QS
2546RunShellCommand(\r
2547 IN CONST CHAR16 *CmdLine,\r
2548 OUT EFI_STATUS *CommandStatus\r
a405b86d 2549 )\r
2550{\r
2551 EFI_STATUS Status;\r
a405b86d 2552 CHAR16 *CleanOriginal;\r
806c49db
JC
2553 CHAR16 *FirstParameter;\r
2554 CHAR16 *TempWalker;\r
2555 SHELL_OPERATION_TYPES Type;\r
a405b86d 2556\r
2557 ASSERT(CmdLine != NULL);\r
2558 if (StrLen(CmdLine) == 0) {\r
2559 return (EFI_SUCCESS);\r
2560 }\r
2561\r
a405b86d 2562 Status = EFI_SUCCESS;\r
2563 CleanOriginal = NULL;\r
a405b86d 2564\r
2565 CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
532691c8 2566 if (CleanOriginal == NULL) {\r
2567 return (EFI_OUT_OF_RESOURCES);\r
2568 }\r
284e034f 2569\r
ad2bc854 2570 TrimSpaces(&CleanOriginal);\r
a405b86d 2571\r
284e034f 2572 //\r
d41a79a0
CP
2573 // NULL out comments (leveraged from RunScriptFileHandle() ).\r
2574 // The # character on a line is used to denote that all characters on the same line\r
2575 // and to the right of the # are to be ignored by the shell.\r
d08a5464 2576 // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.\r
d41a79a0
CP
2577 //\r
2578 for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {\r
2579 if (*TempWalker == L'^') {\r
2580 if (*(TempWalker + 1) == L'#') {\r
4b6b543e 2581 TempWalker++;\r
d41a79a0
CP
2582 }\r
2583 } else if (*TempWalker == L'#') {\r
2584 *TempWalker = CHAR_NULL;\r
2585 }\r
2586 }\r
2587\r
2588 TrimSpaces(&CleanOriginal);\r
2589\r
2590 //\r
284e034f
CP
2591 // Handle case that passed in command line is just 1 or more " " characters.\r
2592 //\r
2593 if (StrLen (CleanOriginal) == 0) {\r
806c49db 2594 SHELL_FREE_NON_NULL(CleanOriginal);\r
284e034f
CP
2595 return (EFI_SUCCESS);\r
2596 }\r
2597\r
12a27a6d 2598 Status = ProcessCommandLineToFinal(&CleanOriginal);\r
ca53c0af 2599 if (EFI_ERROR(Status)) {\r
806c49db 2600 SHELL_FREE_NON_NULL(CleanOriginal);\r
ca53c0af 2601 return (Status);\r
a405b86d 2602 }\r
2603\r
a405b86d 2604 //\r
d08a5464 2605 // We don't do normal processing with a split command line (output from one command input to another)\r
a405b86d 2606 //\r
1ef61d03 2607 if (ContainsSplit(CleanOriginal)) {\r
a308e058 2608 Status = ProcessNewSplitCommandLine(CleanOriginal);\r
806c49db
JC
2609 SHELL_FREE_NON_NULL(CleanOriginal);\r
2610 return (Status);\r
2611 } \r
5b5cd144 2612\r
806c49db
JC
2613 //\r
2614 // We need the first parameter information so we can determine the operation type\r
2615 //\r
2616 FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));\r
2617 if (FirstParameter == NULL) {\r
2618 SHELL_FREE_NON_NULL(CleanOriginal);\r
2619 return (EFI_OUT_OF_RESOURCES);\r
2620 }\r
2621 TempWalker = CleanOriginal;\r
d1c275c6 2622 if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CleanOriginal), TRUE))) {\r
14030c5c
JC
2623 //\r
2624 // Depending on the first parameter we change the behavior\r
2625 //\r
2626 switch (Type = GetOperationType(FirstParameter)) {\r
2627 case File_Sys_Change:\r
2628 Status = ChangeMappedDrive (FirstParameter);\r
2629 break;\r
2630 case Internal_Command:\r
2631 case Script_File_Name:\r
2632 case Efi_Application:\r
490ce43d 2633 Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus);\r
14030c5c
JC
2634 break;\r
2635 default:\r
2636 //\r
2637 // Whatever was typed, it was invalid.\r
2638 //\r
2639 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2640 SetLastError(SHELL_NOT_FOUND);\r
2641 break;\r
2642 }\r
2643 } else {\r
2644 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2645 SetLastError(SHELL_NOT_FOUND);\r
a405b86d 2646 }\r
806c49db 2647 \r
ca53c0af 2648 SHELL_FREE_NON_NULL(CleanOriginal);\r
806c49db 2649 SHELL_FREE_NON_NULL(FirstParameter);\r
a405b86d 2650\r
2651 return (Status);\r
2652}\r
2653\r
490ce43d
QS
2654/**\r
2655 Function will process and run a command line.\r
2656\r
2657 This will determine if the command line represents an internal shell \r
2658 command or dispatch an external application.\r
2659\r
2660 @param[in] CmdLine The command line to parse.\r
2661\r
2662 @retval EFI_SUCCESS The command was completed.\r
2663 @retval EFI_ABORTED The command's operation was aborted.\r
2664**/\r
2665EFI_STATUS\r
2666EFIAPI\r
2667RunCommand(\r
2668 IN CONST CHAR16 *CmdLine\r
2669 )\r
2670{\r
2671 return (RunShellCommand(CmdLine, NULL));\r
2672}\r
2673\r
2674\r
a405b86d 2675STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};\r
2676/**\r
d08a5464 2677 Function determines if the CommandName COULD be a valid command. It does not determine whether\r
a405b86d 2678 this is a valid command. It only checks for invalid characters.\r
2679\r
2680 @param[in] CommandName The name to check\r
2681\r
2682 @retval TRUE CommandName could be a command name\r
2683 @retval FALSE CommandName could not be a valid command name\r
2684**/\r
2685BOOLEAN\r
2686EFIAPI\r
2687IsValidCommandName(\r
2688 IN CONST CHAR16 *CommandName\r
2689 )\r
2690{\r
2691 UINTN Count;\r
2692 if (CommandName == NULL) {\r
2693 ASSERT(FALSE);\r
2694 return (FALSE);\r
2695 }\r
2696 for ( Count = 0\r
2697 ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])\r
2698 ; Count++\r
2699 ){\r
2700 if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {\r
2701 return (FALSE);\r
2702 }\r
2703 }\r
2704 return (TRUE);\r
2705}\r
2706\r
2707/**\r
2708 Function to process a NSH script file via SHELL_FILE_HANDLE.\r
2709\r
2710 @param[in] Handle The handle to the already opened file.\r
2711 @param[in] Name The name of the script file.\r
2712\r
d08a5464 2713 @retval EFI_SUCCESS the script completed successfully\r
a405b86d 2714**/\r
2715EFI_STATUS\r
2716EFIAPI\r
2717RunScriptFileHandle (\r
a308e058
RN
2718 IN SHELL_FILE_HANDLE Handle,\r
2719 IN CONST CHAR16 *Name\r
a405b86d 2720 )\r
2721{\r
2722 EFI_STATUS Status;\r
2723 SCRIPT_FILE *NewScriptFile;\r
2724 UINTN LoopVar;\r
4dc0d578 2725 UINTN PrintBuffSize;\r
a405b86d 2726 CHAR16 *CommandLine;\r
2727 CHAR16 *CommandLine2;\r
2728 CHAR16 *CommandLine3;\r
2729 SCRIPT_COMMAND_LIST *LastCommand;\r
2730 BOOLEAN Ascii;\r
2731 BOOLEAN PreScriptEchoState;\r
848997b5 2732 BOOLEAN PreCommandEchoState;\r
a405b86d 2733 CONST CHAR16 *CurDir;\r
2734 UINTN LineCount;\r
b6b22b13 2735 CHAR16 LeString[50];\r
14810d6b 2736 LIST_ENTRY OldBufferList;\r
a405b86d 2737\r
2738 ASSERT(!ShellCommandGetScriptExit());\r
2739\r
2740 PreScriptEchoState = ShellCommandGetEchoState();\r
4dc0d578 2741 PrintBuffSize = PcdGet16(PcdShellPrintBufferSize);\r
a405b86d 2742\r
2743 NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));\r
3c865f20 2744 if (NewScriptFile == NULL) {\r
2745 return (EFI_OUT_OF_RESOURCES);\r
2746 }\r
a405b86d 2747\r
2748 //\r
2749 // Set up the name\r
2750 //\r
2751 ASSERT(NewScriptFile->ScriptName == NULL);\r
2752 NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);\r
3c865f20 2753 if (NewScriptFile->ScriptName == NULL) {\r
2754 DeleteScriptFileStruct(NewScriptFile);\r
2755 return (EFI_OUT_OF_RESOURCES);\r
2756 }\r
a405b86d 2757\r
2758 //\r
2759 // Save the parameters (used to replace %0 to %9 later on)\r
2760 //\r
2761 NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;\r
2762 if (NewScriptFile->Argc != 0) {\r
2763 NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));\r
3c865f20 2764 if (NewScriptFile->Argv == NULL) {\r
2765 DeleteScriptFileStruct(NewScriptFile);\r
2766 return (EFI_OUT_OF_RESOURCES);\r
2767 }\r
a405b86d 2768 for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {\r
2769 ASSERT(NewScriptFile->Argv[LoopVar] == NULL);\r
2770 NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);\r
3c865f20 2771 if (NewScriptFile->Argv[LoopVar] == NULL) {\r
2772 DeleteScriptFileStruct(NewScriptFile);\r
2773 return (EFI_OUT_OF_RESOURCES);\r
2774 }\r
a405b86d 2775 }\r
2776 } else {\r
2777 NewScriptFile->Argv = NULL;\r
2778 }\r
2779\r
2780 InitializeListHead(&NewScriptFile->CommandList);\r
2781 InitializeListHead(&NewScriptFile->SubstList);\r
2782\r
2783 //\r
2784 // Now build the list of all script commands.\r
2785 //\r
2786 LineCount = 0;\r
2787 while(!ShellFileHandleEof(Handle)) {\r
2788 CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);\r
2789 LineCount++;\r
4922715d
JC
2790 if (CommandLine == NULL || StrLen(CommandLine) == 0 || CommandLine[0] == '#') {\r
2791 SHELL_FREE_NON_NULL(CommandLine);\r
a405b86d 2792 continue;\r
2793 }\r
2794 NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));\r
3c865f20 2795 if (NewScriptFile->CurrentCommand == NULL) {\r
4922715d 2796 SHELL_FREE_NON_NULL(CommandLine);\r
3c865f20 2797 DeleteScriptFileStruct(NewScriptFile);\r
2798 return (EFI_OUT_OF_RESOURCES);\r
2799 }\r
a405b86d 2800\r
2801 NewScriptFile->CurrentCommand->Cl = CommandLine;\r
2802 NewScriptFile->CurrentCommand->Data = NULL;\r
2803 NewScriptFile->CurrentCommand->Line = LineCount;\r
2804\r
2805 InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
2806 }\r
2807\r
2808 //\r
2809 // Add this as the topmost script file\r
2810 //\r
2811 ShellCommandSetNewScript (NewScriptFile);\r
2812\r
2813 //\r
2814 // Now enumerate through the commands and run each one.\r
2815 //\r
4dc0d578 2816 CommandLine = AllocateZeroPool(PrintBuffSize);\r
3c865f20 2817 if (CommandLine == NULL) {\r
2818 DeleteScriptFileStruct(NewScriptFile);\r
2819 return (EFI_OUT_OF_RESOURCES);\r
2820 }\r
4dc0d578 2821 CommandLine2 = AllocateZeroPool(PrintBuffSize);\r
3c865f20 2822 if (CommandLine2 == NULL) {\r
2823 FreePool(CommandLine);\r
2824 DeleteScriptFileStruct(NewScriptFile);\r
2825 return (EFI_OUT_OF_RESOURCES);\r
2826 }\r
a405b86d 2827\r
2828 for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)\r
2829 ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)\r
2830 ; // conditional increment in the body of the loop\r
2831 ){\r
3c865f20 2832 ASSERT(CommandLine2 != NULL);\r
4dc0d578
QS
2833 StrnCpyS( CommandLine2, \r
2834 PrintBuffSize/sizeof(CHAR16), \r
2835 NewScriptFile->CurrentCommand->Cl,\r
2836 PrintBuffSize/sizeof(CHAR16) - 1\r
e75390f0 2837 );\r
a405b86d 2838\r
14810d6b
QS
2839 SaveBufferList(&OldBufferList);\r
2840\r
a405b86d 2841 //\r
2842 // NULL out comments\r
2843 //\r
2844 for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {\r
2845 if (*CommandLine3 == L'^') {\r
4b6b543e 2846 if ( *(CommandLine3+1) == L':') {\r
a405b86d 2847 CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));\r
4b6b543e
QS
2848 } else if (*(CommandLine3+1) == L'#') {\r
2849 CommandLine3++;\r
a405b86d 2850 }\r
2851 } else if (*CommandLine3 == L'#') {\r
2852 *CommandLine3 = CHAR_NULL;\r
2853 }\r
2854 }\r
2855\r
2856 if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {\r
2857 //\r
2858 // Due to variability in starting the find and replace action we need to have both buffers the same.\r
2859 //\r
4dc0d578
QS
2860 StrnCpyS( CommandLine, \r
2861 PrintBuffSize/sizeof(CHAR16), \r
2862 CommandLine2,\r
2863 PrintBuffSize/sizeof(CHAR16) - 1\r
e75390f0 2864 );\r
a405b86d 2865\r
2866 //\r
2867 // Remove the %0 to %9 from the command line (if we have some arguments)\r
2868 //\r
2869 if (NewScriptFile->Argv != NULL) {\r
2870 switch (NewScriptFile->Argc) {\r
2871 default:\r
d1c275c6 2872 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);\r
a405b86d 2873 ASSERT_EFI_ERROR(Status);\r
2874 case 9:\r
d1c275c6 2875 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);\r
a405b86d 2876 ASSERT_EFI_ERROR(Status);\r
2877 case 8:\r
d1c275c6 2878 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);\r
a405b86d 2879 ASSERT_EFI_ERROR(Status);\r
2880 case 7:\r
d1c275c6 2881 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);\r
a405b86d 2882 ASSERT_EFI_ERROR(Status);\r
2883 case 6:\r
d1c275c6 2884 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);\r
a405b86d 2885 ASSERT_EFI_ERROR(Status);\r
2886 case 5:\r
d1c275c6 2887 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);\r
a405b86d 2888 ASSERT_EFI_ERROR(Status);\r
2889 case 4:\r
d1c275c6 2890 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);\r
a405b86d 2891 ASSERT_EFI_ERROR(Status);\r
2892 case 3:\r
d1c275c6 2893 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);\r
a405b86d 2894 ASSERT_EFI_ERROR(Status);\r
2895 case 2:\r
d1c275c6 2896 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);\r
a405b86d 2897 ASSERT_EFI_ERROR(Status);\r
2898 case 1:\r
d1c275c6 2899 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);\r
a405b86d 2900 ASSERT_EFI_ERROR(Status);\r
2901 break;\r
2902 case 0:\r
2903 break;\r
2904 }\r
2905 }\r
4dc0d578
QS
2906 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);\r
2907 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);\r
2908 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);\r
2909 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);\r
2910 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);\r
2911 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);\r
2912 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);\r
2913 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);\r
2914 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);\r
2915\r
2916 StrnCpyS( CommandLine2, \r
2917 PrintBuffSize/sizeof(CHAR16), \r
2918 CommandLine,\r
2919 PrintBuffSize/sizeof(CHAR16) - 1\r
e75390f0 2920 );\r
a405b86d 2921\r
2922 LastCommand = NewScriptFile->CurrentCommand;\r
2923\r
2924 for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);\r
2925\r
3c865f20 2926 if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {\r
a405b86d 2927 //\r
2928 // This line is a goto target / label\r
2929 //\r
2930 } else {\r
2931 if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {\r
848997b5 2932 if (CommandLine3[0] == L'@') {\r
2933 //\r
2934 // We need to save the current echo state\r
2935 // and disable echo for just this command.\r
2936 //\r
2937 PreCommandEchoState = ShellCommandGetEchoState();\r
2938 ShellCommandSetEchoState(FALSE);\r
a308e058 2939 Status = RunCommand(CommandLine3+1);\r
848997b5 2940\r
2941 //\r
284e034f 2942 // If command was "@echo -off" or "@echo -on" then don't restore echo state\r
848997b5 2943 //\r
284e034f
CP
2944 if (StrCmp (L"@echo -off", CommandLine3) != 0 &&\r
2945 StrCmp (L"@echo -on", CommandLine3) != 0) {\r
2946 //\r
2947 // Now restore the pre-'@' echo state.\r
2948 //\r
2949 ShellCommandSetEchoState(PreCommandEchoState);\r
2950 }\r
848997b5 2951 } else {\r
0d11446d 2952 if (ShellCommandGetEchoState()) {\r
2953 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
2954 if (CurDir != NULL && StrLen(CurDir) > 1) {\r
2955 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
2956 } else {\r
2957 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
2958 }\r
2959 ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\r
2960 }\r
a308e058 2961 Status = RunCommand(CommandLine3);\r
848997b5 2962 }\r
a405b86d 2963 }\r
2964\r
2965 if (ShellCommandGetScriptExit()) {\r
5b5cd144
CP
2966 //\r
2967 // ShellCommandGetExitCode() always returns a UINT64\r
2968 //\r
a308e058 2969 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());\r
5b5cd144
CP
2970 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
2971 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
b6b22b13 2972\r
2973 ShellCommandRegisterExit(FALSE, 0);\r
a405b86d 2974 Status = EFI_SUCCESS;\r
14810d6b 2975 RestoreBufferList(&OldBufferList);\r
a405b86d 2976 break;\r
2977 }\r
c6a7fef8 2978 if (ShellGetExecutionBreakFlag()) {\r
14810d6b 2979 RestoreBufferList(&OldBufferList);\r
c6a7fef8
ED
2980 break;\r
2981 }\r
a405b86d 2982 if (EFI_ERROR(Status)) {\r
14810d6b 2983 RestoreBufferList(&OldBufferList);\r
a405b86d 2984 break;\r
2985 }\r
2986 if (ShellCommandGetExit()) {\r
14810d6b 2987 RestoreBufferList(&OldBufferList);\r
a405b86d 2988 break;\r
2989 }\r
2990 }\r
2991 //\r
2992 // If that commend did not update the CurrentCommand then we need to advance it...\r
2993 //\r
2994 if (LastCommand == NewScriptFile->CurrentCommand) {\r
2995 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
2996 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
2997 NewScriptFile->CurrentCommand->Reset = TRUE;\r
2998 }\r
2999 }\r
3000 } else {\r
3001 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
3002 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
3003 NewScriptFile->CurrentCommand->Reset = TRUE;\r
3004 }\r
3005 }\r
14810d6b 3006 RestoreBufferList(&OldBufferList);\r
a405b86d 3007 }\r
3008\r
a405b86d 3009\r
3010 FreePool(CommandLine);\r
3011 FreePool(CommandLine2);\r
3012 ShellCommandSetNewScript (NULL);\r
733f138d 3013\r
3014 //\r
3015 // Only if this was the last script reset the state.\r
3016 //\r
3017 if (ShellCommandGetCurrentScriptFile()==NULL) {\r
3018 ShellCommandSetEchoState(PreScriptEchoState);\r
3019 }\r
a405b86d 3020 return (EFI_SUCCESS);\r
3021}\r
3022\r
3023/**\r
3024 Function to process a NSH script file.\r
3025\r
3026 @param[in] ScriptPath Pointer to the script file name (including file system path).\r
e958b946
JC
3027 @param[in] Handle the handle of the script file already opened.\r
3028 @param[in] CmdLine the command line to run.\r
3029 @param[in] ParamProtocol the shell parameters protocol pointer\r
a405b86d 3030\r
d08a5464 3031 @retval EFI_SUCCESS the script completed successfully\r
a405b86d 3032**/\r
3033EFI_STATUS\r
3034EFIAPI\r
3035RunScriptFile (\r
a308e058
RN
3036 IN CONST CHAR16 *ScriptPath,\r
3037 IN SHELL_FILE_HANDLE Handle OPTIONAL,\r
3038 IN CONST CHAR16 *CmdLine,\r
3039 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol\r
a405b86d 3040 )\r
3041{\r
3042 EFI_STATUS Status;\r
3043 SHELL_FILE_HANDLE FileHandle;\r
e958b946
JC
3044 UINTN Argc;\r
3045 CHAR16 **Argv;\r
a405b86d 3046\r
3047 if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {\r
3048 return (EFI_INVALID_PARAMETER);\r
3049 }\r
3050\r
e958b946
JC
3051 //\r
3052 // get the argc and argv updated for scripts\r
3053 //\r
d1c275c6 3054 Status = UpdateArgcArgv(ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc);\r
e958b946 3055 if (!EFI_ERROR(Status)) {\r
a405b86d 3056\r
e958b946
JC
3057 if (Handle == NULL) {\r
3058 //\r
3059 // open the file\r
3060 //\r
3061 Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
3062 if (!EFI_ERROR(Status)) {\r
3063 //\r
3064 // run it\r
3065 //\r
a308e058 3066 Status = RunScriptFileHandle(FileHandle, ScriptPath);\r
a405b86d 3067\r
e958b946
JC
3068 //\r
3069 // now close the file\r
3070 //\r
3071 ShellCloseFile(&FileHandle);\r
3072 }\r
3073 } else {\r
a308e058 3074 Status = RunScriptFileHandle(Handle, ScriptPath);\r
e958b946
JC
3075 }\r
3076 }\r
3077\r
3078 //\r
d08a5464 3079 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.\r
e958b946
JC
3080 // This is safe even if the update API failed. In this case, it may be a no-op.\r
3081 //\r
3082 RestoreArgcArgv(ParamProtocol, &Argv, &Argc);\r
a405b86d 3083\r
3084 return (Status);\r
3085}\r
00534bc3
JC
3086\r
3087/**\r
d08a5464 3088 Return the pointer to the first occurrence of any character from a list of characters.\r
00534bc3
JC
3089\r
3090 @param[in] String the string to parse\r
3091 @param[in] CharacterList the list of character to look for\r
3092 @param[in] EscapeCharacter An escape character to skip\r
3093\r
3094 @return the location of the first character in the string\r
3095 @retval CHAR_NULL no instance of any character in CharacterList was found in String\r
3096**/\r
3097CONST CHAR16*\r
3098EFIAPI\r
3099FindFirstCharacter(\r
3100 IN CONST CHAR16 *String,\r
3101 IN CONST CHAR16 *CharacterList,\r
3102 IN CONST CHAR16 EscapeCharacter\r
3103 )\r
3104{\r
3105 UINT32 WalkChar;\r
3106 UINT32 WalkStr;\r
3107\r
3108 for (WalkStr = 0; WalkStr < StrLen(String); WalkStr++) {\r
3109 if (String[WalkStr] == EscapeCharacter) {\r
3110 WalkStr++;\r
3111 continue;\r
3112 }\r
3113 for (WalkChar = 0; WalkChar < StrLen(CharacterList); WalkChar++) {\r
3114 if (String[WalkStr] == CharacterList[WalkChar]) {\r
3115 return (&String[WalkStr]);\r
3116 }\r
3117 }\r
3118 }\r
3119 return (String + StrLen(String));\r
88ff5ba7 3120}\r