]> git.proxmox.com Git - mirror_edk2.git/blame - ShellPkg/Application/Shell/Shell.c
ShellPkg: Fix line responsible for deleting “cwd” variable when exit shell.
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
CommitLineData
a405b86d 1/** @file\r
2 This is THE shell (application)\r
3\r
5559a41a 4 Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>\r
a405b86d 5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "Shell.h"\r
16\r
17//\r
18// Initialize the global structure\r
19//\r
20SHELL_INFO ShellInfoObject = {\r
21 NULL,\r
22 NULL,\r
23 FALSE,\r
24 FALSE,\r
25 {\r
ce68d3bc
SZ
26 {{\r
27 0,\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 }},\r
a405b86d 37 0,\r
38 NULL,\r
39 NULL\r
40 },\r
ce68d3bc 41 {{NULL, NULL}, NULL},\r
a405b86d 42 {\r
ce68d3bc 43 {{NULL, NULL}, NULL},\r
a405b86d 44 0,\r
45 0,\r
46 TRUE\r
47 },\r
48 NULL,\r
49 0,\r
50 NULL,\r
51 NULL,\r
52 NULL,\r
53 NULL,\r
54 NULL,\r
ce68d3bc
SZ
55 {{NULL, NULL}, NULL, NULL},\r
56 {{NULL, NULL}, NULL, NULL},\r
57 NULL,\r
58 NULL,\r
59 NULL,\r
60 NULL,\r
8be0ba36 61 NULL,\r
62 NULL,\r
63 NULL,\r
a49f6a2f 64 NULL,\r
65 FALSE\r
a405b86d 66};\r
67\r
68STATIC CONST CHAR16 mScriptExtension[] = L".NSH";\r
69STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";\r
3c865f20 70STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh";\r
a405b86d 71\r
733f138d 72/**\r
73 Function to start monitoring for CTRL-S using SimpleTextInputEx. This \r
74 feature's enabled state was not known when the shell initially launched.\r
75\r
76 @retval EFI_SUCCESS The feature is enabled.\r
77 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.\r
78**/\r
79EFI_STATUS\r
80EFIAPI\r
81InternalEfiShellStartCtrlSMonitor(\r
82 VOID\r
83 )\r
84{\r
85 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;\r
86 EFI_KEY_DATA KeyData;\r
87 EFI_STATUS Status;\r
88\r
89 Status = gBS->OpenProtocol(\r
90 gST->ConsoleInHandle,\r
91 &gEfiSimpleTextInputExProtocolGuid,\r
92 (VOID**)&SimpleEx,\r
93 gImageHandle,\r
94 NULL,\r
95 EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
96 if (EFI_ERROR(Status)) {\r
97 ShellPrintHiiEx(\r
98 -1, \r
99 -1, \r
100 NULL,\r
101 STRING_TOKEN (STR_SHELL_NO_IN_EX),\r
102 ShellInfoObject.HiiHandle);\r
103 return (EFI_SUCCESS);\r
104 }\r
105\r
106 KeyData.KeyState.KeyToggleState = 0;\r
107 KeyData.Key.ScanCode = 0;\r
108 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
109 KeyData.Key.UnicodeChar = L's';\r
110\r
111 Status = SimpleEx->RegisterKeyNotify(\r
112 SimpleEx,\r
113 &KeyData,\r
114 NotificationFunction,\r
115 &ShellInfoObject.CtrlSNotifyHandle1);\r
116 \r
117 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
118 if (!EFI_ERROR(Status)) {\r
119 Status = SimpleEx->RegisterKeyNotify(\r
120 SimpleEx,\r
121 &KeyData,\r
122 NotificationFunction,\r
123 &ShellInfoObject.CtrlSNotifyHandle2);\r
124 }\r
125 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
126 KeyData.Key.UnicodeChar = 19;\r
127\r
128 if (!EFI_ERROR(Status)) {\r
129 Status = SimpleEx->RegisterKeyNotify(\r
130 SimpleEx,\r
131 &KeyData,\r
132 NotificationFunction,\r
133 &ShellInfoObject.CtrlSNotifyHandle2);\r
134 } \r
135 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
136 if (!EFI_ERROR(Status)) {\r
137 Status = SimpleEx->RegisterKeyNotify(\r
138 SimpleEx,\r
139 &KeyData,\r
140 NotificationFunction,\r
141 &ShellInfoObject.CtrlSNotifyHandle2);\r
142 }\r
143 return (Status);\r
144}\r
145\r
146\r
147\r
a405b86d 148/**\r
149 The entry point for the application.\r
150\r
151 @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
152 @param[in] SystemTable A pointer to the EFI System Table.\r
153\r
154 @retval EFI_SUCCESS The entry point is executed successfully.\r
155 @retval other Some error occurs when executing this entry point.\r
156\r
157**/\r
158EFI_STATUS\r
159EFIAPI\r
160UefiMain (\r
161 IN EFI_HANDLE ImageHandle,\r
162 IN EFI_SYSTEM_TABLE *SystemTable\r
163 )\r
164{\r
a49f6a2f 165 EFI_STATUS Status;\r
166 CHAR16 *TempString;\r
167 UINTN Size;\r
168 EFI_HANDLE ConInHandle;\r
169 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;\r
a405b86d 170\r
171 if (PcdGet8(PcdShellSupportLevel) > 3) {\r
172 return (EFI_UNSUPPORTED);\r
173 }\r
174\r
175 //\r
176 // Clear the screen\r
177 //\r
178 Status = gST->ConOut->ClearScreen(gST->ConOut);\r
3c865f20 179 if (EFI_ERROR(Status)) {\r
180 return (Status);\r
181 }\r
a405b86d 182\r
183 //\r
184 // Populate the global structure from PCDs\r
185 //\r
186 ShellInfoObject.ImageDevPath = NULL;\r
187 ShellInfoObject.FileDevPath = NULL;\r
188 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);\r
189 ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool(PcdShellInsertModeDefault);\r
190 ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount );\r
191\r
192 //\r
193 // verify we dont allow for spec violation\r
194 //\r
195 ASSERT(ShellInfoObject.LogScreenCount >= 3);\r
196\r
197 //\r
198 // Initialize the LIST ENTRY objects...\r
199 //\r
200 InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);\r
201 InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);\r
202 InitializeListHead(&ShellInfoObject.SplitList.Link);\r
203\r
204 //\r
205 // Check PCDs for optional features that are not implemented yet.\r
206 //\r
207 if ( PcdGetBool(PcdShellSupportOldProtocols)\r
208 || !FeaturePcdGet(PcdShellRequireHiiPlatform)\r
209 || FeaturePcdGet(PcdShellSupportFrameworkHii)\r
210 ) {\r
211 return (EFI_UNSUPPORTED);\r
212 }\r
213\r
214 //\r
215 // turn off the watchdog timer\r
216 //\r
217 gBS->SetWatchdogTimer (0, 0, 0, NULL);\r
218\r
219 //\r
220 // install our console logger. This will keep a log of the output for back-browsing\r
221 //\r
222 Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);\r
3c865f20 223 if (!EFI_ERROR(Status)) {\r
224 //\r
225 // Enable the cursor to be visible\r
226 //\r
227 gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
a405b86d 228\r
3c865f20 229 //\r
230 // If supporting EFI 1.1 we need to install HII protocol\r
231 // only do this if PcdShellRequireHiiPlatform == FALSE\r
232 //\r
233 // remove EFI_UNSUPPORTED check above when complete.\r
234 ///@todo add support for Framework HII\r
a405b86d 235\r
3c865f20 236 //\r
237 // install our (solitary) HII package\r
238 //\r
239 ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);\r
a405b86d 240 if (ShellInfoObject.HiiHandle == NULL) {\r
3c865f20 241 if (PcdGetBool(PcdShellSupportFrameworkHii)) {\r
242 ///@todo Add our package into Framework HII\r
243 }\r
244 if (ShellInfoObject.HiiHandle == NULL) {\r
245 return (EFI_NOT_STARTED);\r
246 }\r
a405b86d 247 }\r
a405b86d 248\r
3c865f20 249 //\r
250 // create and install the EfiShellParametersProtocol\r
251 //\r
252 Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);\r
253 ASSERT_EFI_ERROR(Status);\r
254 ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);\r
a405b86d 255\r
3c865f20 256 //\r
257 // create and install the EfiShellProtocol\r
258 //\r
259 Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);\r
260 ASSERT_EFI_ERROR(Status);\r
261 ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);\r
a405b86d 262\r
3c865f20 263 //\r
264 // Now initialize the shell library (it requires Shell Parameters protocol)\r
265 //\r
266 Status = ShellInitialize();\r
267 ASSERT_EFI_ERROR(Status);\r
a405b86d 268\r
3c865f20 269 Status = CommandInit();\r
270 ASSERT_EFI_ERROR(Status);\r
a405b86d 271\r
3c865f20 272 //\r
273 // Check the command line\r
274 //\r
275 Status = ProcessCommandLine();\r
a405b86d 276\r
3c865f20 277 //\r
278 // If shell support level is >= 1 create the mappings and paths\r
279 //\r
280 if (PcdGet8(PcdShellSupportLevel) >= 1) {\r
281 Status = ShellCommandCreateInitialMappingsAndPaths();\r
282 }\r
a405b86d 283\r
3c865f20 284 //\r
285 // save the device path for the loaded image and the device path for the filepath (under loaded image)\r
286 // These are where to look for the startup.nsh file\r
287 //\r
288 Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);\r
a405b86d 289 ASSERT_EFI_ERROR(Status);\r
a405b86d 290\r
3c865f20 291 //\r
292 // Display the version\r
293 //\r
294 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {\r
295 ShellPrintHiiEx (\r
296 0,\r
297 gST->ConOut->Mode->CursorRow,\r
298 NULL,\r
299 STRING_TOKEN (STR_VER_OUTPUT_MAIN),\r
300 ShellInfoObject.HiiHandle,\r
301 SupportLevel[PcdGet8(PcdShellSupportLevel)],\r
302 gEfiShellProtocol->MajorVersion,\r
303 gEfiShellProtocol->MinorVersion,\r
304 (gST->Hdr.Revision&0xffff0000)>>16,\r
305 (gST->Hdr.Revision&0x0000ffff),\r
306 gST->FirmwareVendor,\r
307 gST->FirmwareRevision\r
308 );\r
309 }\r
a405b86d 310\r
3c865f20 311 //\r
312 // Display the mapping\r
313 //\r
314 if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {\r
315 Status = RunCommand(L"map");\r
316 ASSERT_EFI_ERROR(Status);\r
317 }\r
a405b86d 318\r
3c865f20 319 //\r
320 // init all the built in alias'\r
321 //\r
322 Status = SetBuiltInAlias();\r
323 ASSERT_EFI_ERROR(Status);\r
a405b86d 324\r
3c865f20 325 //\r
326 // Initialize environment variables\r
327 //\r
328 if (ShellCommandGetProfileList() != NULL) {\r
329 Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);\r
330 ASSERT_EFI_ERROR(Status);\r
331 }\r
a405b86d 332\r
3c865f20 333 Size = 100;\r
334 TempString = AllocateZeroPool(Size);\r
a405b86d 335\r
3c865f20 336 UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));\r
337 Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);\r
338 ASSERT_EFI_ERROR(Status);\r
a405b86d 339\r
3c865f20 340 UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);\r
341 Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);\r
342 ASSERT_EFI_ERROR(Status);\r
a405b86d 343\r
3c865f20 344 UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);\r
345 Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);\r
346 ASSERT_EFI_ERROR(Status);\r
a405b86d 347\r
3c865f20 348 FreePool(TempString);\r
a405b86d 349\r
3c865f20 350 if (!EFI_ERROR(Status)) {\r
351 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
a405b86d 352 //\r
3c865f20 353 // Set up the event for CTRL-C monitoring...\r
a405b86d 354 //\r
8be0ba36 355 Status = InernalEfiShellStartMonitor();\r
3c865f20 356 }\r
357\r
733f138d 358 if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
359 //\r
360 // Set up the event for CTRL-S monitoring...\r
361 //\r
362 Status = InternalEfiShellStartCtrlSMonitor();\r
363 }\r
364\r
a49f6a2f 365 if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
366 //\r
367 // close off the gST->ConIn\r
368 //\r
369 OldConIn = gST->ConIn;\r
370 ConInHandle = gST->ConsoleInHandle;\r
371 gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);\r
372 } else {\r
373 OldConIn = NULL;\r
374 ConInHandle = NULL;\r
375 }\r
376\r
3c865f20 377 if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {\r
a405b86d 378 //\r
3c865f20 379 // process the startup script or launch the called app.\r
a405b86d 380 //\r
3c865f20 381 Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
382 }\r
a405b86d 383\r
5f88eebe 384 if (!ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
a405b86d 385 //\r
3c865f20 386 // begin the UI waiting loop\r
a405b86d 387 //\r
3c865f20 388 do {\r
389 //\r
390 // clean out all the memory allocated for CONST <something> * return values\r
391 // between each shell prompt presentation\r
392 //\r
393 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){\r
394 FreeBufferList(&ShellInfoObject.BufferToFreeList);\r
395 }\r
396\r
397 //\r
398 // Reset page break back to default.\r
399 //\r
400 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);\r
401 ShellInfoObject.ConsoleInfo->Enabled = TRUE;\r
402 ShellInfoObject.ConsoleInfo->RowCounter = 0;\r
403\r
8be0ba36 404 //\r
405 // Reset the CTRL-C event (yes we ignore the return values)\r
406 //\r
407 Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);\r
408\r
3c865f20 409 //\r
410 // Display Prompt\r
411 //\r
412 Status = DoShellPrompt();\r
413 } while (!ShellCommandGetExit());\r
414 }\r
a49f6a2f 415 if (OldConIn != NULL && ConInHandle != NULL) {\r
416 CloseSimpleTextInOnFile (gST->ConIn);\r
417 gST->ConIn = OldConIn;\r
418 gST->ConsoleInHandle = ConInHandle;\r
419 }\r
a405b86d 420 }\r
421 }\r
3c865f20 422\r
a405b86d 423 //\r
424 // uninstall protocols / free memory / etc...\r
425 //\r
426 if (ShellInfoObject.UserBreakTimer != NULL) {\r
427 gBS->CloseEvent(ShellInfoObject.UserBreakTimer);\r
428 DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);\r
429 }\r
a405b86d 430 if (ShellInfoObject.ImageDevPath != NULL) {\r
431 FreePool(ShellInfoObject.ImageDevPath);\r
432 DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);\r
433 }\r
434 if (ShellInfoObject.FileDevPath != NULL) {\r
435 FreePool(ShellInfoObject.FileDevPath);\r
436 DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;);\r
437 }\r
438 if (ShellInfoObject.NewShellParametersProtocol != NULL) {\r
439 CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol);\r
440 DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);\r
441 }\r
442 if (ShellInfoObject.NewEfiShellProtocol != NULL){\r
8be0ba36 443 if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){\r
92670471 444 InternalEfiShellSetEnv(L"cwd", NULL, TRUE);\r
8be0ba36 445 }\r
a405b86d 446 CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);\r
447 DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);\r
448 }\r
449\r
450 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){\r
451 FreeBufferList(&ShellInfoObject.BufferToFreeList);\r
452 }\r
453\r
454 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){\r
3c865f20 455 ASSERT(FALSE); ///@todo finish this de-allocation.\r
a405b86d 456 }\r
457\r
458 if (ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
459 FreePool(ShellInfoObject.ShellInitSettings.FileName);\r
460 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);\r
461 }\r
462\r
463 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
464 FreePool(ShellInfoObject.ShellInitSettings.FileOptions);\r
465 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);\r
466 }\r
467\r
468 if (ShellInfoObject.HiiHandle != NULL) {\r
469 HiiRemovePackages(ShellInfoObject.HiiHandle);\r
470 DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);\r
471 }\r
472\r
473 if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){\r
474 FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);\r
475 }\r
476\r
477 ASSERT(ShellInfoObject.ConsoleInfo != NULL);\r
478 if (ShellInfoObject.ConsoleInfo != NULL) {\r
479 ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);\r
480 FreePool(ShellInfoObject.ConsoleInfo);\r
481 DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);\r
482 }\r
483\r
b6b22b13 484 if (ShellCommandGetExit()) {\r
485 return ((EFI_STATUS)ShellCommandGetExitCode());\r
486 }\r
a405b86d 487 return (Status);\r
488}\r
489\r
490/**\r
491 Sets all the alias' that were registered with the ShellCommandLib library.\r
492\r
493 @retval EFI_SUCCESS all init commands were run sucessfully.\r
494**/\r
495EFI_STATUS\r
496EFIAPI\r
497SetBuiltInAlias(\r
498 )\r
499{\r
500 EFI_STATUS Status;\r
501 CONST ALIAS_LIST *List;\r
502 ALIAS_LIST *Node;\r
503\r
504 //\r
505 // Get all the commands we want to run\r
506 //\r
507 List = ShellCommandGetInitAliasList();\r
508\r
509 //\r
510 // for each command in the List\r
511 //\r
512 for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)\r
513 ; !IsNull (&List->Link, &Node->Link)\r
514 ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)\r
515 ){\r
516 //\r
517 // install the alias'\r
518 //\r
519 Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);\r
520 ASSERT_EFI_ERROR(Status);\r
521 }\r
522 return (EFI_SUCCESS);\r
523}\r
524\r
525/**\r
526 Internal function to determine if 2 command names are really the same.\r
527\r
528 @param[in] Command1 The pointer to the first command name.\r
529 @param[in] Command2 The pointer to the second command name.\r
530\r
531 @retval TRUE The 2 command names are the same.\r
532 @retval FALSE The 2 command names are not the same.\r
533**/\r
534BOOLEAN\r
535EFIAPI\r
536IsCommand(\r
537 IN CONST CHAR16 *Command1,\r
538 IN CONST CHAR16 *Command2\r
539 )\r
540{\r
541 if (StringNoCaseCompare(&Command1, &Command2) == 0) {\r
542 return (TRUE);\r
543 }\r
544 return (FALSE);\r
545}\r
546\r
547/**\r
548 Internal function to determine if a command is a script only command.\r
549\r
550 @param[in] CommandName The pointer to the command name.\r
551\r
552 @retval TRUE The command is a script only command.\r
553 @retval FALSE The command is not a script only command.\r
554**/\r
555BOOLEAN\r
556EFIAPI\r
557IsScriptOnlyCommand(\r
558 IN CONST CHAR16 *CommandName\r
559 )\r
560{\r
561 if (IsCommand(CommandName, L"for")\r
562 ||IsCommand(CommandName, L"endfor")\r
563 ||IsCommand(CommandName, L"if")\r
564 ||IsCommand(CommandName, L"else")\r
565 ||IsCommand(CommandName, L"endif")\r
566 ||IsCommand(CommandName, L"goto")) {\r
567 return (TRUE);\r
568 }\r
569 return (FALSE);\r
570}\r
571\r
a405b86d 572/**\r
573 This function will populate the 2 device path protocol parameters based on the\r
574 global gImageHandle. The DevPath will point to the device path for the handle that has\r
575 loaded image protocol installed on it. The FilePath will point to the device path\r
576 for the file that was loaded.\r
577\r
4ff7e37b
ED
578 @param[in, out] DevPath On a sucessful return the device path to the loaded image.\r
579 @param[in, out] FilePath On a sucessful return the device path to the file.\r
a405b86d 580\r
581 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.\r
582 @retval other A error from gBS->HandleProtocol.\r
583\r
584 @sa HandleProtocol\r
585**/\r
586EFI_STATUS\r
587EFIAPI\r
588GetDevicePathsForImageAndFile (\r
589 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,\r
590 IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath\r
591 )\r
592{\r
593 EFI_STATUS Status;\r
594 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
595 EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;\r
596\r
597 ASSERT(DevPath != NULL);\r
598 ASSERT(FilePath != NULL);\r
599\r
600 Status = gBS->OpenProtocol (\r
601 gImageHandle,\r
602 &gEfiLoadedImageProtocolGuid,\r
603 (VOID**)&LoadedImage,\r
604 gImageHandle,\r
605 NULL,\r
606 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
607 );\r
608 if (!EFI_ERROR (Status)) {\r
609 Status = gBS->OpenProtocol (\r
610 LoadedImage->DeviceHandle,\r
611 &gEfiDevicePathProtocolGuid,\r
612 (VOID**)&ImageDevicePath,\r
613 gImageHandle,\r
614 NULL,\r
615 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
616 );\r
617 if (!EFI_ERROR (Status)) {\r
618 *DevPath = DuplicateDevicePath (ImageDevicePath);\r
619 *FilePath = DuplicateDevicePath (LoadedImage->FilePath);\r
a49f6a2f 620 gBS->CloseProtocol(\r
621 LoadedImage->DeviceHandle,\r
622 &gEfiDevicePathProtocolGuid,\r
623 gImageHandle,\r
624 NULL);\r
a405b86d 625 }\r
626 gBS->CloseProtocol(\r
627 gImageHandle,\r
628 &gEfiLoadedImageProtocolGuid,\r
629 gImageHandle,\r
630 NULL);\r
631 }\r
632 return (Status);\r
633}\r
634\r
635STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {\r
636 {L"-nostartup", TypeFlag},\r
637 {L"-startup", TypeFlag},\r
638 {L"-noconsoleout", TypeFlag},\r
639 {L"-noconsolein", TypeFlag},\r
640 {L"-nointerrupt", TypeFlag},\r
641 {L"-nomap", TypeFlag},\r
642 {L"-noversion", TypeFlag},\r
643 {L"-startup", TypeFlag},\r
644 {L"-delay", TypeValue},\r
645 {NULL, TypeMax}\r
646 };\r
a49f6a2f 647\r
a405b86d 648/**\r
649 Process all Uefi Shell 2.0 command line options.\r
650\r
651 see Uefi Shell 2.0 section 3.2 for full details.\r
652\r
653 the command line must resemble the following:\r
654\r
655 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]\r
656\r
657 ShellOpt-options Options which control the initialization behavior of the shell.\r
658 These options are read from the EFI global variable "ShellOpt"\r
659 and are processed before options or file-name.\r
660\r
661 options Options which control the initialization behavior of the shell.\r
662\r
663 file-name The name of a UEFI shell application or script to be executed\r
664 after initialization is complete. By default, if file-name is\r
665 specified, then -nostartup is implied. Scripts are not supported\r
666 by level 0.\r
667\r
668 file-name-options The command-line options that are passed to file-name when it\r
669 is invoked.\r
670\r
671 This will initialize the ShellInfoObject.ShellInitSettings global variable.\r
672\r
673 @retval EFI_SUCCESS The variable is initialized.\r
674**/\r
675EFI_STATUS\r
676EFIAPI\r
677ProcessCommandLine(\r
678 VOID\r
679 )\r
680{\r
681 EFI_STATUS Status;\r
682 LIST_ENTRY *Package;\r
683 UINTN Size;\r
684 CONST CHAR16 *TempConst;\r
685 UINTN Count;\r
686 UINTN LoopVar;\r
687 CHAR16 *ProblemParam;\r
733f138d 688 UINT64 Intermediate;\r
a405b86d 689\r
690 Package = NULL;\r
691 ProblemParam = NULL;\r
692 \r
693 Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);\r
694\r
695 Count = 1;\r
696 Size = 0;\r
697 TempConst = ShellCommandLineGetRawValue(Package, Count++);\r
698 if (TempConst != NULL && StrLen(TempConst)) {\r
733f138d 699 ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(TempConst));\r
3c865f20 700 if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
701 return (EFI_OUT_OF_RESOURCES);\r
702 }\r
a405b86d 703 StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);\r
704 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
705 for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
706 if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {\r
707 LoopVar++;\r
708 //\r
709 // We found the file... add the rest of the params...\r
710 //\r
711 for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
712 ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
713 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
714 &Size,\r
715 L" ",\r
716 0);\r
3c865f20 717 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
718 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
719 return (EFI_OUT_OF_RESOURCES);\r
720 }\r
a405b86d 721 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
722 &Size,\r
723 gEfiShellParametersProtocol->Argv[LoopVar],\r
724 0);\r
3c865f20 725 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
726 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
727 return (EFI_OUT_OF_RESOURCES);\r
728 }\r
a405b86d 729 }\r
730 }\r
731 }\r
732 } else {\r
733 ShellCommandLineFreeVarList(Package);\r
734 Package = NULL;\r
735 Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);\r
736 if (EFI_ERROR(Status)) {\r
737 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);\r
738 FreePool(ProblemParam);\r
739 ShellCommandLineFreeVarList(Package);\r
740 return (EFI_INVALID_PARAMETER);\r
741 }\r
742 }\r
743\r
744 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = ShellCommandLineGetFlag(Package, L"-startup");\r
745 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = ShellCommandLineGetFlag(Package, L"-nostartup");\r
746 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");\r
747 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = ShellCommandLineGetFlag(Package, L"-noconsolein");\r
748 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = ShellCommandLineGetFlag(Package, L"-nointerrupt");\r
749 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = ShellCommandLineGetFlag(Package, L"-nomap");\r
750 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = ShellCommandLineGetFlag(Package, L"-noversion");\r
751 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = ShellCommandLineGetFlag(Package, L"-delay");\r
752\r
733f138d 753 ShellInfoObject.ShellInitSettings.Delay = 5;\r
754\r
755 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
756 ShellInfoObject.ShellInitSettings.Delay = 0;\r
757 } else if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {\r
a405b86d 758 TempConst = ShellCommandLineGetValue(Package, L"-delay");\r
33c031ee 759 if (TempConst != NULL && *TempConst == L':') {\r
733f138d 760 TempConst++;\r
761 }\r
762 if (TempConst != NULL && !EFI_ERROR(ShellConvertStringToUint64(TempConst, &Intermediate, FALSE, FALSE))) {\r
763 ShellInfoObject.ShellInitSettings.Delay = (UINTN)Intermediate;\r
a405b86d 764 }\r
a405b86d 765 }\r
a405b86d 766 ShellCommandLineFreeVarList(Package);\r
767\r
768 return (Status);\r
769}\r
770\r
771/**\r
772 Handles all interaction with the default startup script.\r
773\r
774 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.\r
775\r
776 @param ImagePath the path to the image for shell. first place to look for the startup script\r
777 @param FilePath the path to the file for shell. second place to look for the startup script.\r
778\r
779 @retval EFI_SUCCESS the variable is initialized.\r
780**/\r
781EFI_STATUS\r
782EFIAPI\r
783DoStartupScript(\r
784 EFI_DEVICE_PATH_PROTOCOL *ImagePath,\r
785 EFI_DEVICE_PATH_PROTOCOL *FilePath\r
786 )\r
787{\r
788 EFI_STATUS Status;\r
789 UINTN Delay;\r
790 EFI_INPUT_KEY Key;\r
791 SHELL_FILE_HANDLE FileHandle;\r
792 EFI_DEVICE_PATH_PROTOCOL *NewPath;\r
793 EFI_DEVICE_PATH_PROTOCOL *NamePath;\r
794 CHAR16 *FileStringPath;\r
733f138d 795 CHAR16 *TempSpot;\r
a405b86d 796 UINTN NewSize;\r
733f138d 797 CONST CHAR16 *MapName;\r
a405b86d 798\r
799 Key.UnicodeChar = CHAR_NULL;\r
800 Key.ScanCode = 0;\r
801 FileHandle = NULL;\r
802\r
803 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
804 //\r
805 // launch something else instead\r
806 //\r
807 NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);\r
808 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
809 NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);\r
810 }\r
811 FileStringPath = AllocateZeroPool(NewSize);\r
3c865f20 812 if (FileStringPath == NULL) {\r
813 return (EFI_OUT_OF_RESOURCES);\r
814 }\r
a405b86d 815 StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);\r
816 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
3c865f20 817 StrCat(FileStringPath, L" ");\r
a405b86d 818 StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);\r
819 }\r
820 Status = RunCommand(FileStringPath);\r
821 FreePool(FileStringPath);\r
822 return (Status);\r
823\r
824 }\r
825\r
826 //\r
827 // for shell level 0 we do no scripts\r
828 // Without the Startup bit overriding we allow for nostartup to prevent scripts\r
829 //\r
830 if ( (PcdGet8(PcdShellSupportLevel) < 1)\r
831 || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)\r
832 ){\r
833 return (EFI_SUCCESS);\r
834 }\r
835\r
5559a41a 836 gST->ConOut->EnableCursor(gST->ConOut, FALSE);\r
a405b86d 837 //\r
838 // print out our warning and see if they press a key\r
839 //\r
733f138d 840 for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay * 10\r
841 ; Delay != 0 && EFI_ERROR(Status)\r
a405b86d 842 ; Delay--\r
843 ){\r
844 ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay/10);\r
845 gBS->Stall (100000);\r
733f138d 846 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
847 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
848 }\r
a405b86d 849 }\r
850 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);\r
5559a41a 851 gST->ConOut->EnableCursor(gST->ConOut, TRUE);\r
a405b86d 852\r
853 //\r
854 // ESC was pressed\r
855 //\r
856 if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {\r
857 return (EFI_SUCCESS);\r
858 }\r
859\r
a405b86d 860 //\r
733f138d 861 // Try the first location (must be file system)\r
a405b86d 862 //\r
733f138d 863 MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);\r
864 if (MapName != NULL) {\r
865 FileStringPath = NULL;\r
866 NewSize = 0;\r
867 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);\r
532691c8 868 if (FileStringPath == NULL) {\r
869 Status = EFI_OUT_OF_RESOURCES;\r
870 } else {\r
871 TempSpot = StrStr(FileStringPath, L";");\r
872 if (TempSpot != NULL) {\r
873 *TempSpot = CHAR_NULL;\r
874 }\r
875 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);\r
876 PathRemoveLastItem(FileStringPath);\r
877 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);\r
878 Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);\r
879 FreePool(FileStringPath);\r
733f138d 880 }\r
733f138d 881 }\r
a405b86d 882 if (EFI_ERROR(Status)) {\r
733f138d 883 NamePath = FileDevicePath (NULL, mStartupScript);\r
884 NewPath = AppendDevicePathNode (ImagePath, NamePath);\r
885 FreePool(NamePath);\r
886\r
a405b86d 887 //\r
733f138d 888 // Try the location\r
a405b86d 889 //\r
a405b86d 890 Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
733f138d 891 FreePool(NewPath);\r
a405b86d 892 }\r
a405b86d 893 //\r
894 // If we got a file, run it\r
895 //\r
733f138d 896 if (!EFI_ERROR(Status) && FileHandle != NULL) {\r
3c865f20 897 Status = RunScriptFileHandle (FileHandle, mStartupScript);\r
a405b86d 898 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);\r
899 } else {\r
3c865f20 900 FileStringPath = ShellFindFilePath(mStartupScript);\r
901 if (FileStringPath == NULL) {\r
902 //\r
903 // we return success since we dont need to have a startup script\r
904 //\r
905 Status = EFI_SUCCESS;\r
906 ASSERT(FileHandle == NULL);\r
907 } else {\r
908 Status = RunScriptFile(FileStringPath);\r
909 FreePool(FileStringPath);\r
910 }\r
a405b86d 911 }\r
912\r
a405b86d 913\r
914 return (Status);\r
915}\r
916\r
917/**\r
918 Function to perform the shell prompt looping. It will do a single prompt,\r
919 dispatch the result, and then return. It is expected that the caller will\r
920 call this function in a loop many times.\r
921\r
922 @retval EFI_SUCCESS\r
923 @retval RETURN_ABORTED\r
924**/\r
925EFI_STATUS\r
926EFIAPI\r
927DoShellPrompt (\r
928 VOID\r
929 )\r
930{\r
931 UINTN Column;\r
932 UINTN Row;\r
933 CHAR16 *CmdLine;\r
934 CONST CHAR16 *CurDir;\r
935 UINTN BufferSize;\r
936 EFI_STATUS Status;\r
937\r
938 CurDir = NULL;\r
939\r
940 //\r
941 // Get screen setting to decide size of the command line buffer\r
942 //\r
943 gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);\r
944 BufferSize = Column * Row * sizeof (CHAR16);\r
945 CmdLine = AllocateZeroPool (BufferSize);\r
946 if (CmdLine == NULL) {\r
947 return EFI_OUT_OF_RESOURCES;\r
948 }\r
949\r
950 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
951\r
952 //\r
953 // Prompt for input\r
954 //\r
955 gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);\r
956\r
957 if (CurDir != NULL && StrLen(CurDir) > 1) {\r
958 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
959 } else {\r
960 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
961 }\r
962\r
963 //\r
964 // Read a line from the console\r
965 //\r
966 Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);\r
967\r
968 //\r
969 // Null terminate the string and parse it\r
970 //\r
971 if (!EFI_ERROR (Status)) {\r
972 CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;\r
973 Status = RunCommand(CmdLine);\r
974 }\r
975\r
976 //\r
977 // Done with this command\r
978 //\r
979 FreePool (CmdLine);\r
980 return Status;\r
981}\r
982\r
983/**\r
984 Add a buffer to the Buffer To Free List for safely returning buffers to other\r
985 places without risking letting them modify internal shell information.\r
986\r
987 @param Buffer Something to pass to FreePool when the shell is exiting.\r
988**/\r
989VOID*\r
990EFIAPI\r
991AddBufferToFreeList(\r
992 VOID *Buffer\r
993 )\r
994{\r
995 BUFFER_LIST *BufferListEntry;\r
996\r
997 if (Buffer == NULL) {\r
998 return (NULL);\r
999 }\r
1000\r
1001 BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));\r
1002 ASSERT(BufferListEntry != NULL);\r
1003 BufferListEntry->Buffer = Buffer;\r
1004 InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);\r
1005 return (Buffer);\r
1006}\r
1007\r
1008/**\r
1009 Add a buffer to the Line History List\r
1010\r
1011 @param Buffer The line buffer to add.\r
1012**/\r
1013VOID\r
1014EFIAPI\r
1015AddLineToCommandHistory(\r
1016 IN CONST CHAR16 *Buffer\r
1017 )\r
1018{\r
1019 BUFFER_LIST *Node;\r
1020\r
1021 Node = AllocateZeroPool(sizeof(BUFFER_LIST));\r
1022 ASSERT(Node != NULL);\r
1023 Node->Buffer = AllocateZeroPool(StrSize(Buffer));\r
1024 ASSERT(Node->Buffer != NULL);\r
1025 StrCpy(Node->Buffer, Buffer);\r
1026\r
1027 InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
1028}\r
1029\r
1030/**\r
1031 Checks if a string is an alias for another command. If yes, then it replaces the alias name\r
1032 with the correct command name.\r
1033\r
4ff7e37b
ED
1034 @param[in, out] CommandString Upon entry the potential alias. Upon return the\r
1035 command name if it was an alias. If it was not\r
1036 an alias it will be unchanged. This function may\r
1037 change the buffer to fit the command name.\r
a405b86d 1038\r
1039 @retval EFI_SUCCESS The name was changed.\r
1040 @retval EFI_SUCCESS The name was not an alias.\r
1041 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
1042**/\r
1043EFI_STATUS\r
1044EFIAPI\r
1045ShellConvertAlias(\r
1046 IN OUT CHAR16 **CommandString\r
1047 )\r
1048{\r
1049 CONST CHAR16 *NewString;\r
1050\r
1051 NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);\r
1052 if (NewString == NULL) {\r
1053 return (EFI_SUCCESS);\r
1054 }\r
1055 FreePool(*CommandString);\r
733f138d 1056 *CommandString = AllocateZeroPool(StrSize(NewString));\r
a405b86d 1057 if (*CommandString == NULL) {\r
1058 return (EFI_OUT_OF_RESOURCES);\r
1059 }\r
1060 StrCpy(*CommandString, NewString);\r
1061 return (EFI_SUCCESS);\r
1062}\r
1063\r
1064/**\r
1065 Function allocates a new command line and replaces all instances of environment\r
1066 variable names that are correctly preset to their values.\r
1067\r
1068 If the return value is not NULL the memory must be caller freed.\r
1069\r
1070 @param[in] OriginalCommandLine The original command line\r
1071\r
1072 @retval NULL An error ocurred.\r
1073 @return The new command line with no environment variables present.\r
1074**/\r
1075CHAR16*\r
1076EFIAPI\r
1077ShellConvertVariables (\r
1078 IN CONST CHAR16 *OriginalCommandLine\r
1079 )\r
1080{\r
1081 CONST CHAR16 *MasterEnvList;\r
1082 UINTN NewSize;\r
1083 CHAR16 *NewCommandLine1;\r
1084 CHAR16 *NewCommandLine2;\r
1085 CHAR16 *Temp;\r
1086 UINTN ItemSize;\r
1087 CHAR16 *ItemTemp;\r
1088 SCRIPT_FILE *CurrentScriptFile;\r
1089 ALIAS_LIST *AliasListNode;\r
1090\r
1091 ASSERT(OriginalCommandLine != NULL);\r
1092\r
1093 ItemSize = 0;\r
1094 NewSize = StrSize(OriginalCommandLine);\r
1095 CurrentScriptFile = ShellCommandGetCurrentScriptFile();\r
1096 Temp = NULL;\r
1097\r
1098 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?\r
1099\r
1100 //\r
1101 // calculate the size required for the post-conversion string...\r
1102 //\r
1103 if (CurrentScriptFile != NULL) {\r
1104 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1105 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1106 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1107 ){\r
1108 for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)\r
1109 ; Temp != NULL\r
1110 ; Temp = StrStr(Temp+1, AliasListNode->Alias)\r
1111 ){\r
1112 //\r
1113 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)\r
1114 //\r
1115 if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {\r
1116 NewSize += StrSize(AliasListNode->CommandString);\r
1117 }\r
1118 }\r
1119 }\r
1120 }\r
1121\r
1122 for (MasterEnvList = EfiShellGetEnv(NULL)\r
1123 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL\r
1124 ; MasterEnvList += StrLen(MasterEnvList) + 1\r
1125 ){\r
1126 if (StrSize(MasterEnvList) > ItemSize) {\r
1127 ItemSize = StrSize(MasterEnvList);\r
1128 }\r
1129 for (Temp = StrStr(OriginalCommandLine, MasterEnvList)\r
1130 ; Temp != NULL\r
1131 ; Temp = StrStr(Temp+1, MasterEnvList)\r
1132 ){\r
1133 //\r
1134 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)\r
1135 //\r
1136 if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&\r
1137 ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {\r
1138 NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));\r
1139 }\r
1140 }\r
1141 }\r
1142\r
1143 //\r
1144 // Quick out if none were found...\r
1145 //\r
1146 if (NewSize == StrSize(OriginalCommandLine)) {\r
1147 ASSERT(Temp == NULL);\r
1148 Temp = StrnCatGrow(&Temp, NULL, OriginalCommandLine, 0);\r
1149 return (Temp);\r
1150 }\r
1151\r
1152 //\r
1153 // now do the replacements...\r
1154 //\r
1155 NewCommandLine1 = AllocateZeroPool(NewSize);\r
1156 NewCommandLine2 = AllocateZeroPool(NewSize);\r
1157 ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));\r
3c865f20 1158 if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {\r
1159 SHELL_FREE_NON_NULL(NewCommandLine1);\r
1160 SHELL_FREE_NON_NULL(NewCommandLine2);\r
1161 SHELL_FREE_NON_NULL(ItemTemp);\r
1162 return (NULL);\r
1163 }\r
a405b86d 1164 StrCpy(NewCommandLine1, OriginalCommandLine);\r
1165 for (MasterEnvList = EfiShellGetEnv(NULL)\r
1166 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL\r
1167 ; MasterEnvList += StrLen(MasterEnvList) + 1\r
1168 ){\r
1169 StrCpy(ItemTemp, L"%");\r
1170 StrCat(ItemTemp, MasterEnvList);\r
1171 StrCat(ItemTemp, L"%");\r
1172 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);\r
1173 StrCpy(NewCommandLine1, NewCommandLine2);\r
1174 }\r
1175 if (CurrentScriptFile != NULL) {\r
1176 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1177 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1178 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1179 ){\r
1180 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);\r
1181 StrCpy(NewCommandLine1, NewCommandLine2);\r
1182 }\r
1183 }\r
1184\r
1185 FreePool(NewCommandLine2);\r
1186 FreePool(ItemTemp);\r
1187\r
1188 return (NewCommandLine1);\r
1189}\r
1190\r
1191/**\r
1192 Internal function to run a command line with pipe usage.\r
1193\r
1194 @param[in] CmdLine The pointer to the command line.\r
1195 @param[in] StdIn The pointer to the Standard input.\r
1196 @param[in] StdOut The pointer to the Standard output.\r
1197\r
1198 @retval EFI_SUCCESS The split command is executed successfully.\r
1199 @retval other Some error occurs when executing the split command.\r
1200**/\r
1201EFI_STATUS\r
1202EFIAPI\r
1203RunSplitCommand(\r
1204 IN CONST CHAR16 *CmdLine,\r
1205 IN SHELL_FILE_HANDLE *StdIn,\r
1206 IN SHELL_FILE_HANDLE *StdOut\r
1207 )\r
1208{\r
1209 EFI_STATUS Status;\r
1210 CHAR16 *NextCommandLine;\r
1211 CHAR16 *OurCommandLine;\r
1212 UINTN Size1;\r
1213 UINTN Size2;\r
1214 SPLIT_LIST *Split;\r
1215 SHELL_FILE_HANDLE *TempFileHandle;\r
1216 BOOLEAN Unicode;\r
1217\r
1218 ASSERT(StdOut == NULL);\r
1219\r
1220 ASSERT(StrStr(CmdLine, L"|") != NULL);\r
1221\r
1222 Status = EFI_SUCCESS;\r
1223 NextCommandLine = NULL;\r
1224 OurCommandLine = NULL;\r
1225 Size1 = 0;\r
1226 Size2 = 0;\r
1227\r
1228 NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);\r
1229 OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine);\r
532691c8 1230\r
1231 if (NextCommandLine == NULL || OurCommandLine == NULL) {\r
1232 SHELL_FREE_NON_NULL(OurCommandLine);\r
1233 SHELL_FREE_NON_NULL(NextCommandLine);\r
1234 return (EFI_OUT_OF_RESOURCES);\r
19216573 1235 } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {\r
1236 SHELL_FREE_NON_NULL(OurCommandLine);\r
1237 SHELL_FREE_NON_NULL(NextCommandLine);\r
1238 return (EFI_INVALID_PARAMETER);\r
1239 } else if (NextCommandLine[0] != CHAR_NULL &&\r
a405b86d 1240 NextCommandLine[0] == L'a' &&\r
1241 NextCommandLine[1] == L' '\r
1242 ){\r
1243 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
1244 Unicode = FALSE;\r
1245 } else {\r
1246 Unicode = TRUE;\r
1247 }\r
1248\r
1249\r
1250 //\r
1251 // make a SPLIT_LIST item and add to list\r
1252 //\r
1253 Split = AllocateZeroPool(sizeof(SPLIT_LIST));\r
1254 ASSERT(Split != NULL);\r
1255 Split->SplitStdIn = StdIn;\r
1256 Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);\r
1257 ASSERT(Split->SplitStdOut != NULL);\r
1258 InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
1259\r
a405b86d 1260 Status = RunCommand(OurCommandLine);\r
1261\r
1262 //\r
1263 // move the output from the first to the in to the second.\r
1264 //\r
1265 TempFileHandle = Split->SplitStdOut;\r
1266 if (Split->SplitStdIn == StdIn) {\r
1267 Split->SplitStdOut = NULL;\r
1268 } else {\r
1269 Split->SplitStdOut = Split->SplitStdIn;\r
1270 }\r
1271 Split->SplitStdIn = TempFileHandle;\r
1272 ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);\r
1273\r
1274 if (!EFI_ERROR(Status)) {\r
1275 Status = RunCommand(NextCommandLine);\r
1276 }\r
1277\r
1278 //\r
1279 // remove the top level from the ScriptList\r
1280 //\r
1281 ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);\r
1282 RemoveEntryList(&Split->Link);\r
1283\r
1284 //\r
1285 // Note that the original StdIn is now the StdOut...\r
1286 //\r
1287 if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {\r
1288 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));\r
1289 }\r
1290 if (Split->SplitStdIn != NULL) {\r
1291 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));\r
1292 }\r
1293\r
1294 FreePool(Split);\r
1295 FreePool(NextCommandLine);\r
1296 FreePool(OurCommandLine);\r
1297\r
1298 return (Status);\r
1299}\r
1300\r
1301/**\r
1302 Function will process and run a command line.\r
1303\r
1304 This will determine if the command line represents an internal shell \r
1305 command or dispatch an external application.\r
1306\r
1307 @param[in] CmdLine The command line to parse.\r
1308\r
1309 @retval EFI_SUCCESS The command was completed.\r
1310 @retval EFI_ABORTED The command's operation was aborted.\r
1311**/\r
1312EFI_STATUS\r
1313EFIAPI\r
1314RunCommand(\r
1315 IN CONST CHAR16 *CmdLine\r
1316 )\r
1317{\r
1318 EFI_STATUS Status;\r
64c7a749 1319 EFI_STATUS StatusCode;\r
a405b86d 1320 CHAR16 *CommandName;\r
1321 SHELL_STATUS ShellStatus;\r
1322 UINTN Argc;\r
1323 CHAR16 **Argv;\r
1324 BOOLEAN LastError;\r
1325 CHAR16 LeString[11];\r
1326 CHAR16 *PostAliasCmdLine;\r
1327 UINTN PostAliasSize;\r
1328 CHAR16 *PostVariableCmdLine;\r
1329 CHAR16 *CommandWithPath;\r
733f138d 1330 CONST EFI_DEVICE_PATH_PROTOCOL *DevPath;\r
a405b86d 1331 CONST CHAR16 *TempLocation;\r
1332 CONST CHAR16 *TempLocation2;\r
1333 SHELL_FILE_HANDLE OriginalStdIn;\r
1334 SHELL_FILE_HANDLE OriginalStdOut;\r
1335 SHELL_FILE_HANDLE OriginalStdErr;\r
8be0ba36 1336 SYSTEM_TABLE_INFO OriginalSystemTableInfo;\r
a405b86d 1337 CHAR16 *TempLocation3;\r
1338 UINTN Count;\r
1339 UINTN Count2;\r
1340 CHAR16 *CleanOriginal;\r
1341 SPLIT_LIST *Split;\r
1342\r
1343 ASSERT(CmdLine != NULL);\r
1344 if (StrLen(CmdLine) == 0) {\r
1345 return (EFI_SUCCESS);\r
1346 }\r
1347\r
1348 CommandName = NULL;\r
1349 PostVariableCmdLine = NULL;\r
1350 PostAliasCmdLine = NULL;\r
1351 CommandWithPath = NULL;\r
1352 DevPath = NULL;\r
1353 Status = EFI_SUCCESS;\r
1354 CleanOriginal = NULL;\r
1355 Split = NULL;\r
1356\r
1357 CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
532691c8 1358 if (CleanOriginal == NULL) {\r
1359 return (EFI_OUT_OF_RESOURCES);\r
1360 }\r
a405b86d 1361 while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {\r
1362 CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;\r
1363 }\r
1364 while (CleanOriginal[0] == L' ') {\r
1365 CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));\r
1366 }\r
1367\r
1368 CommandName = NULL;\r
1369 if (StrStr(CleanOriginal, L" ") == NULL){\r
1370 StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);\r
1371 } else {\r
1372 StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);\r
1373 }\r
1374\r
1375 ASSERT(PostAliasCmdLine == NULL);\r
1376 if (!ShellCommandIsCommandOnList(CommandName)) {\r
1377 //\r
1378 // Convert via alias\r
1379 //\r
1380 Status = ShellConvertAlias(&CommandName);\r
1381 PostAliasSize = 0;\r
1382 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);\r
1383 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);\r
1384 ASSERT_EFI_ERROR(Status);\r
1385 } else {\r
1386 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);\r
1387 }\r
1388\r
1389 if (CleanOriginal != NULL) {\r
1390 FreePool(CleanOriginal);\r
1391 CleanOriginal = NULL;\r
1392 }\r
1393\r
1394 if (CommandName != NULL) {\r
1395 FreePool(CommandName);\r
1396 CommandName = NULL;\r
1397 }\r
1398\r
1399 PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);\r
1400\r
1401 //\r
1402 // we can now free the modified by alias command line\r
1403 //\r
1404 if (PostAliasCmdLine != NULL) {\r
1405 FreePool(PostAliasCmdLine);\r
1406 PostAliasCmdLine = NULL;\r
1407 }\r
1408\r
3c865f20 1409 if (PostVariableCmdLine == NULL) {\r
1410 return (EFI_OUT_OF_RESOURCES);\r
1411 }\r
1412\r
a405b86d 1413 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
1414 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
1415 }\r
1416 while (PostVariableCmdLine[0] == L' ') {\r
1417 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
1418 }\r
1419\r
1420 //\r
1421 // We dont do normal processing with a split command line (output from one command input to another)\r
1422 //\r
1423 TempLocation3 = NULL;\r
1424 if (StrStr(PostVariableCmdLine, L"|") != NULL) {\r
1425 for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {\r
1426 if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') {\r
1427 TempLocation3++;\r
1428 } else if (*TempLocation3 == L'|') {\r
1429 break;\r
1430 }\r
1431 }\r
1432 }\r
1433 if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {\r
1434 //\r
1435 // are we in an existing split???\r
1436 //\r
1437 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
1438 Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
1439 }\r
1440\r
1441 if (Split == NULL) {\r
1442 Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);\r
1443 } else {\r
1444 Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
1445 }\r
19216573 1446 if (EFI_ERROR(Status)) {\r
1447 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, PostVariableCmdLine);\r
1448 }\r
a405b86d 1449 } else {\r
1450\r
1451 //\r
1452 // If this is a mapped drive change handle that...\r
1453 //\r
1454 if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {\r
1455 Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);\r
1456 if (EFI_ERROR(Status)) {\r
1457 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);\r
1458 }\r
1459 FreePool(PostVariableCmdLine);\r
1460 return (Status);\r
1461 }\r
1462\r
1463 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...\r
1464 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...\r
1465\r
1466\r
1467\r
8be0ba36 1468 Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
a405b86d 1469 if (EFI_ERROR(Status)) {\r
733f138d 1470 if (Status == EFI_NOT_FOUND) {\r
1471 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);\r
1472 } else {\r
1473 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);\r
1474 }\r
a405b86d 1475 } else {\r
a405b86d 1476 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
1477 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
1478 }\r
1479 while (PostVariableCmdLine[0] == L' ') {\r
1480 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
1481 }\r
1482\r
1483 //\r
1484 // get the argc and argv updated for internal commands\r
1485 //\r
1486 Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);\r
1487 ASSERT_EFI_ERROR(Status);\r
1488\r
1489 for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {\r
1490 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]\r
1491 || (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)\r
1492 ) {\r
1493 //\r
1494 // We need to redo the arguments since a parameter was -?\r
1495 // move them all down 1 to the end, then up one then replace the first with help\r
1496 //\r
1497 FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);\r
1498 ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;\r
1499 for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {\r
1500 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];\r
1501 }\r
1502 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;\r
1503 for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {\r
1504 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];\r
1505 }\r
1506 ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;\r
1507 ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);\r
1508 break;\r
1509 }\r
1510 }\r
1511\r
1512 //\r
1513 // command or file?\r
1514 //\r
1515 if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
1516 //\r
1517 // Run the command (which was converted if it was an alias)\r
1518 //\r
1519 if (!EFI_ERROR(Status)) {\r
1520 Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);\r
1521 ASSERT_EFI_ERROR(Status);\r
1522 UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", ShellStatus);\r
1523 DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
1524 if (LastError) {\r
1525 InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
1526 }\r
1527 //\r
1528 // Pass thru the exitcode from the app.\r
1529 //\r
1530 if (ShellCommandGetExit()) {\r
1531 Status = ShellStatus;\r
1532 } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
1533 Status = EFI_ABORTED;\r
1534 }\r
1535 }\r
1536 } else {\r
1537 //\r
1538 // run an external file (or script)\r
1539 //\r
1540 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {\r
1541 ASSERT (CommandWithPath == NULL);\r
1542 if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {\r
1543 CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
1544 }\r
1545 }\r
1546 if (CommandWithPath == NULL) {\r
1547 CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);\r
1548 }\r
1549 if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {\r
1550 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);\r
1551 } else {\r
1552 //\r
1553 // Check if it's a NSH (script) file.\r
1554 //\r
1555 TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;\r
1556 TempLocation2 = mScriptExtension;\r
1557 if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {\r
1558 Status = RunScriptFile (CommandWithPath);\r
1559 } else {\r
1560 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
1561 ASSERT(DevPath != NULL);\r
1562 Status = InternalShellExecuteDevicePath(\r
1563 &gImageHandle,\r
1564 DevPath,\r
1565 PostVariableCmdLine,\r
1566 NULL,\r
64c7a749 1567 &StatusCode\r
a405b86d 1568 );\r
64c7a749
ED
1569\r
1570 //\r
1571 // Updatet last error status.\r
1572 //\r
1573 UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", StatusCode);\r
1574 DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
1575 InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
a405b86d 1576 }\r
1577 }\r
1578 }\r
64c7a749
ED
1579\r
1580 //\r
1581 // Print some error info.\r
1582 //\r
1583 if (EFI_ERROR(Status)) {\r
1584 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
1585 }\r
1586\r
a405b86d 1587 CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
1588\r
1589 RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
1590\r
8be0ba36 1591 RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
a405b86d 1592 }\r
1593 if (CommandName != NULL) {\r
1594 if (ShellCommandGetCurrentScriptFile() != NULL && !IsScriptOnlyCommand(CommandName)) {\r
1595 //\r
1596 // if this is NOT a scipt only command return success so the script won't quit.\r
1597 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)\r
1598 //\r
1599 Status = EFI_SUCCESS;\r
1600 }\r
1601 }\r
1602 }\r
1603\r
1604 SHELL_FREE_NON_NULL(CommandName);\r
1605 SHELL_FREE_NON_NULL(CommandWithPath);\r
1606 SHELL_FREE_NON_NULL(PostVariableCmdLine);\r
a405b86d 1607\r
1608 return (Status);\r
1609}\r
1610\r
1611STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};\r
1612/**\r
1613 Function determins if the CommandName COULD be a valid command. It does not determine whether\r
1614 this is a valid command. It only checks for invalid characters.\r
1615\r
1616 @param[in] CommandName The name to check\r
1617\r
1618 @retval TRUE CommandName could be a command name\r
1619 @retval FALSE CommandName could not be a valid command name\r
1620**/\r
1621BOOLEAN\r
1622EFIAPI\r
1623IsValidCommandName(\r
1624 IN CONST CHAR16 *CommandName\r
1625 )\r
1626{\r
1627 UINTN Count;\r
1628 if (CommandName == NULL) {\r
1629 ASSERT(FALSE);\r
1630 return (FALSE);\r
1631 }\r
1632 for ( Count = 0\r
1633 ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])\r
1634 ; Count++\r
1635 ){\r
1636 if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {\r
1637 return (FALSE);\r
1638 }\r
1639 }\r
1640 return (TRUE);\r
1641}\r
1642\r
1643/**\r
1644 Function to process a NSH script file via SHELL_FILE_HANDLE.\r
1645\r
1646 @param[in] Handle The handle to the already opened file.\r
1647 @param[in] Name The name of the script file.\r
1648\r
1649 @retval EFI_SUCCESS the script completed sucessfully\r
1650**/\r
1651EFI_STATUS\r
1652EFIAPI\r
1653RunScriptFileHandle (\r
1654 IN SHELL_FILE_HANDLE Handle,\r
1655 IN CONST CHAR16 *Name\r
1656 )\r
1657{\r
1658 EFI_STATUS Status;\r
1659 SCRIPT_FILE *NewScriptFile;\r
1660 UINTN LoopVar;\r
1661 CHAR16 *CommandLine;\r
1662 CHAR16 *CommandLine2;\r
1663 CHAR16 *CommandLine3;\r
1664 SCRIPT_COMMAND_LIST *LastCommand;\r
1665 BOOLEAN Ascii;\r
1666 BOOLEAN PreScriptEchoState;\r
848997b5 1667 BOOLEAN PreCommandEchoState;\r
a405b86d 1668 CONST CHAR16 *CurDir;\r
1669 UINTN LineCount;\r
b6b22b13 1670 CHAR16 LeString[50];\r
a405b86d 1671\r
1672 ASSERT(!ShellCommandGetScriptExit());\r
1673\r
1674 PreScriptEchoState = ShellCommandGetEchoState();\r
1675\r
1676 NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));\r
3c865f20 1677 if (NewScriptFile == NULL) {\r
1678 return (EFI_OUT_OF_RESOURCES);\r
1679 }\r
a405b86d 1680\r
1681 //\r
1682 // Set up the name\r
1683 //\r
1684 ASSERT(NewScriptFile->ScriptName == NULL);\r
1685 NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);\r
3c865f20 1686 if (NewScriptFile->ScriptName == NULL) {\r
1687 DeleteScriptFileStruct(NewScriptFile);\r
1688 return (EFI_OUT_OF_RESOURCES);\r
1689 }\r
a405b86d 1690\r
1691 //\r
1692 // Save the parameters (used to replace %0 to %9 later on)\r
1693 //\r
1694 NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;\r
1695 if (NewScriptFile->Argc != 0) {\r
1696 NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));\r
3c865f20 1697 if (NewScriptFile->Argv == NULL) {\r
1698 DeleteScriptFileStruct(NewScriptFile);\r
1699 return (EFI_OUT_OF_RESOURCES);\r
1700 }\r
a405b86d 1701 for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {\r
1702 ASSERT(NewScriptFile->Argv[LoopVar] == NULL);\r
1703 NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);\r
3c865f20 1704 if (NewScriptFile->Argv[LoopVar] == NULL) {\r
1705 DeleteScriptFileStruct(NewScriptFile);\r
1706 return (EFI_OUT_OF_RESOURCES);\r
1707 }\r
a405b86d 1708 }\r
1709 } else {\r
1710 NewScriptFile->Argv = NULL;\r
1711 }\r
1712\r
1713 InitializeListHead(&NewScriptFile->CommandList);\r
1714 InitializeListHead(&NewScriptFile->SubstList);\r
1715\r
1716 //\r
1717 // Now build the list of all script commands.\r
1718 //\r
1719 LineCount = 0;\r
1720 while(!ShellFileHandleEof(Handle)) {\r
1721 CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);\r
1722 LineCount++;\r
1723 if (CommandLine == NULL || StrLen(CommandLine) == 0) {\r
1724 continue;\r
1725 }\r
1726 NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));\r
3c865f20 1727 if (NewScriptFile->CurrentCommand == NULL) {\r
1728 DeleteScriptFileStruct(NewScriptFile);\r
1729 return (EFI_OUT_OF_RESOURCES);\r
1730 }\r
a405b86d 1731\r
1732 NewScriptFile->CurrentCommand->Cl = CommandLine;\r
1733 NewScriptFile->CurrentCommand->Data = NULL;\r
1734 NewScriptFile->CurrentCommand->Line = LineCount;\r
1735\r
1736 InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
1737 }\r
1738\r
1739 //\r
1740 // Add this as the topmost script file\r
1741 //\r
1742 ShellCommandSetNewScript (NewScriptFile);\r
1743\r
1744 //\r
1745 // Now enumerate through the commands and run each one.\r
1746 //\r
733f138d 1747 CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
3c865f20 1748 if (CommandLine == NULL) {\r
1749 DeleteScriptFileStruct(NewScriptFile);\r
1750 return (EFI_OUT_OF_RESOURCES);\r
1751 }\r
733f138d 1752 CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
3c865f20 1753 if (CommandLine2 == NULL) {\r
1754 FreePool(CommandLine);\r
1755 DeleteScriptFileStruct(NewScriptFile);\r
1756 return (EFI_OUT_OF_RESOURCES);\r
1757 }\r
a405b86d 1758\r
1759 for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)\r
1760 ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)\r
1761 ; // conditional increment in the body of the loop\r
1762 ){\r
3c865f20 1763 ASSERT(CommandLine2 != NULL);\r
a405b86d 1764 StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);\r
1765\r
1766 //\r
1767 // NULL out comments\r
1768 //\r
1769 for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {\r
1770 if (*CommandLine3 == L'^') {\r
1771 if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {\r
1772 CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));\r
1773 }\r
1774 } else if (*CommandLine3 == L'#') {\r
1775 *CommandLine3 = CHAR_NULL;\r
1776 }\r
1777 }\r
1778\r
1779 if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {\r
1780 //\r
1781 // Due to variability in starting the find and replace action we need to have both buffers the same.\r
1782 //\r
1783 StrCpy(CommandLine, CommandLine2);\r
1784\r
1785 //\r
1786 // Remove the %0 to %9 from the command line (if we have some arguments)\r
1787 //\r
1788 if (NewScriptFile->Argv != NULL) {\r
1789 switch (NewScriptFile->Argc) {\r
1790 default:\r
1791 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);\r
1792 ASSERT_EFI_ERROR(Status);\r
1793 case 9:\r
1794 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);\r
1795 ASSERT_EFI_ERROR(Status);\r
1796 case 8:\r
1797 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);\r
1798 ASSERT_EFI_ERROR(Status);\r
1799 case 7:\r
1800 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);\r
1801 ASSERT_EFI_ERROR(Status);\r
1802 case 6:\r
1803 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);\r
1804 ASSERT_EFI_ERROR(Status);\r
1805 case 5:\r
1806 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);\r
1807 ASSERT_EFI_ERROR(Status);\r
1808 case 4:\r
1809 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);\r
1810 ASSERT_EFI_ERROR(Status);\r
1811 case 3:\r
1812 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);\r
1813 ASSERT_EFI_ERROR(Status);\r
1814 case 2:\r
1815 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);\r
1816 ASSERT_EFI_ERROR(Status);\r
1817 case 1:\r
1818 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);\r
1819 ASSERT_EFI_ERROR(Status);\r
1820 break;\r
1821 case 0:\r
1822 break;\r
1823 }\r
1824 }\r
1825 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);\r
1826 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);\r
1827 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);\r
1828 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);\r
1829 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);\r
1830 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);\r
1831 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);\r
1832 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);\r
1833 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);\r
1834\r
1835 StrCpy(CommandLine2, CommandLine);\r
1836\r
1837 LastCommand = NewScriptFile->CurrentCommand;\r
1838\r
1839 for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);\r
1840\r
3c865f20 1841 if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {\r
a405b86d 1842 //\r
1843 // This line is a goto target / label\r
1844 //\r
1845 } else {\r
1846 if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {\r
848997b5 1847 if (CommandLine3[0] == L'@') {\r
1848 //\r
1849 // We need to save the current echo state\r
1850 // and disable echo for just this command.\r
1851 //\r
1852 PreCommandEchoState = ShellCommandGetEchoState();\r
1853 ShellCommandSetEchoState(FALSE);\r
1854 Status = RunCommand(CommandLine3+1);\r
1855\r
1856 //\r
1857 // Now restore the pre-'@' echo state.\r
1858 //\r
1859 ShellCommandSetEchoState(PreCommandEchoState);\r
1860 } else {\r
0d11446d 1861 if (ShellCommandGetEchoState()) {\r
1862 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
1863 if (CurDir != NULL && StrLen(CurDir) > 1) {\r
1864 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
1865 } else {\r
1866 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
1867 }\r
1868 ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\r
1869 }\r
848997b5 1870 Status = RunCommand(CommandLine3);\r
1871 }\r
a405b86d 1872 }\r
1873\r
1874 if (ShellCommandGetScriptExit()) {\r
b6b22b13 1875 UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%Lx", ShellCommandGetExitCode());\r
1876 DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
1877 InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
1878\r
1879 ShellCommandRegisterExit(FALSE, 0);\r
a405b86d 1880 Status = EFI_SUCCESS;\r
1881 break;\r
1882 }\r
c6a7fef8
ED
1883 if (ShellGetExecutionBreakFlag()) {\r
1884 break;\r
1885 }\r
a405b86d 1886 if (EFI_ERROR(Status)) {\r
1887 break;\r
1888 }\r
1889 if (ShellCommandGetExit()) {\r
1890 break;\r
1891 }\r
1892 }\r
1893 //\r
1894 // If that commend did not update the CurrentCommand then we need to advance it...\r
1895 //\r
1896 if (LastCommand == NewScriptFile->CurrentCommand) {\r
1897 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
1898 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
1899 NewScriptFile->CurrentCommand->Reset = TRUE;\r
1900 }\r
1901 }\r
1902 } else {\r
1903 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
1904 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
1905 NewScriptFile->CurrentCommand->Reset = TRUE;\r
1906 }\r
1907 }\r
1908 }\r
1909\r
a405b86d 1910\r
1911 FreePool(CommandLine);\r
1912 FreePool(CommandLine2);\r
1913 ShellCommandSetNewScript (NULL);\r
733f138d 1914\r
1915 //\r
1916 // Only if this was the last script reset the state.\r
1917 //\r
1918 if (ShellCommandGetCurrentScriptFile()==NULL) {\r
1919 ShellCommandSetEchoState(PreScriptEchoState);\r
1920 }\r
a405b86d 1921 return (EFI_SUCCESS);\r
1922}\r
1923\r
1924/**\r
1925 Function to process a NSH script file.\r
1926\r
1927 @param[in] ScriptPath Pointer to the script file name (including file system path).\r
1928\r
1929 @retval EFI_SUCCESS the script completed sucessfully\r
1930**/\r
1931EFI_STATUS\r
1932EFIAPI\r
1933RunScriptFile (\r
1934 IN CONST CHAR16 *ScriptPath\r
1935 )\r
1936{\r
1937 EFI_STATUS Status;\r
1938 SHELL_FILE_HANDLE FileHandle;\r
1939\r
1940 if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {\r
1941 return (EFI_INVALID_PARAMETER);\r
1942 }\r
1943\r
1944 Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
1945 if (EFI_ERROR(Status)) {\r
1946 return (Status);\r
1947 }\r
1948\r
1949 Status = RunScriptFileHandle(FileHandle, ScriptPath);\r
1950\r
1951 ShellCloseFile(&FileHandle);\r
1952\r
1953 return (Status);\r
1954}\r