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