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