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