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