]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/Shell.c
ShellPkg: fix Pipe usage verification.
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
1 /** @file
2 This is THE shell (application)
3
4 Copyright (c) 2009 - 2011, 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 //
831 // print out our warning and see if they press a key
832 //
833 for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay * 10
834 ; Delay != 0 && EFI_ERROR(Status)
835 ; Delay--
836 ){
837 ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay/10);
838 gBS->Stall (100000);
839 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
840 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
841 }
842 }
843 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
844
845 //
846 // ESC was pressed
847 //
848 if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {
849 return (EFI_SUCCESS);
850 }
851
852 //
853 // Try the first location (must be file system)
854 //
855 MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);
856 if (MapName != NULL) {
857 FileStringPath = NULL;
858 NewSize = 0;
859 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);
860 if (FileStringPath == NULL) {
861 Status = EFI_OUT_OF_RESOURCES;
862 } else {
863 TempSpot = StrStr(FileStringPath, L";");
864 if (TempSpot != NULL) {
865 *TempSpot = CHAR_NULL;
866 }
867 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);
868 PathRemoveLastItem(FileStringPath);
869 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);
870 Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);
871 FreePool(FileStringPath);
872 }
873 }
874 if (EFI_ERROR(Status)) {
875 NamePath = FileDevicePath (NULL, mStartupScript);
876 NewPath = AppendDevicePathNode (ImagePath, NamePath);
877 FreePool(NamePath);
878
879 //
880 // Try the location
881 //
882 Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
883 FreePool(NewPath);
884 }
885 //
886 // If we got a file, run it
887 //
888 if (!EFI_ERROR(Status) && FileHandle != NULL) {
889 Status = RunScriptFileHandle (FileHandle, mStartupScript);
890 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
891 } else {
892 FileStringPath = ShellFindFilePath(mStartupScript);
893 if (FileStringPath == NULL) {
894 //
895 // we return success since we dont need to have a startup script
896 //
897 Status = EFI_SUCCESS;
898 ASSERT(FileHandle == NULL);
899 } else {
900 Status = RunScriptFile(FileStringPath);
901 FreePool(FileStringPath);
902 }
903 }
904
905
906 return (Status);
907 }
908
909 /**
910 Function to perform the shell prompt looping. It will do a single prompt,
911 dispatch the result, and then return. It is expected that the caller will
912 call this function in a loop many times.
913
914 @retval EFI_SUCCESS
915 @retval RETURN_ABORTED
916 **/
917 EFI_STATUS
918 EFIAPI
919 DoShellPrompt (
920 VOID
921 )
922 {
923 UINTN Column;
924 UINTN Row;
925 CHAR16 *CmdLine;
926 CONST CHAR16 *CurDir;
927 UINTN BufferSize;
928 EFI_STATUS Status;
929
930 CurDir = NULL;
931
932 //
933 // Get screen setting to decide size of the command line buffer
934 //
935 gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
936 BufferSize = Column * Row * sizeof (CHAR16);
937 CmdLine = AllocateZeroPool (BufferSize);
938 if (CmdLine == NULL) {
939 return EFI_OUT_OF_RESOURCES;
940 }
941
942 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
943
944 //
945 // Prompt for input
946 //
947 gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
948
949 if (CurDir != NULL && StrLen(CurDir) > 1) {
950 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
951 } else {
952 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
953 }
954
955 //
956 // Read a line from the console
957 //
958 Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
959
960 //
961 // Null terminate the string and parse it
962 //
963 if (!EFI_ERROR (Status)) {
964 CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
965 Status = RunCommand(CmdLine);
966 }
967
968 //
969 // Done with this command
970 //
971 FreePool (CmdLine);
972 return Status;
973 }
974
975 /**
976 Add a buffer to the Buffer To Free List for safely returning buffers to other
977 places without risking letting them modify internal shell information.
978
979 @param Buffer Something to pass to FreePool when the shell is exiting.
980 **/
981 VOID*
982 EFIAPI
983 AddBufferToFreeList(
984 VOID *Buffer
985 )
986 {
987 BUFFER_LIST *BufferListEntry;
988
989 if (Buffer == NULL) {
990 return (NULL);
991 }
992
993 BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));
994 ASSERT(BufferListEntry != NULL);
995 BufferListEntry->Buffer = Buffer;
996 InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
997 return (Buffer);
998 }
999
1000 /**
1001 Add a buffer to the Line History List
1002
1003 @param Buffer The line buffer to add.
1004 **/
1005 VOID
1006 EFIAPI
1007 AddLineToCommandHistory(
1008 IN CONST CHAR16 *Buffer
1009 )
1010 {
1011 BUFFER_LIST *Node;
1012
1013 Node = AllocateZeroPool(sizeof(BUFFER_LIST));
1014 ASSERT(Node != NULL);
1015 Node->Buffer = AllocateZeroPool(StrSize(Buffer));
1016 ASSERT(Node->Buffer != NULL);
1017 StrCpy(Node->Buffer, Buffer);
1018
1019 InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
1020 }
1021
1022 /**
1023 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1024 with the correct command name.
1025
1026 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1027 command name if it was an alias. If it was not
1028 an alias it will be unchanged. This function may
1029 change the buffer to fit the command name.
1030
1031 @retval EFI_SUCCESS The name was changed.
1032 @retval EFI_SUCCESS The name was not an alias.
1033 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1034 **/
1035 EFI_STATUS
1036 EFIAPI
1037 ShellConvertAlias(
1038 IN OUT CHAR16 **CommandString
1039 )
1040 {
1041 CONST CHAR16 *NewString;
1042
1043 NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);
1044 if (NewString == NULL) {
1045 return (EFI_SUCCESS);
1046 }
1047 FreePool(*CommandString);
1048 *CommandString = AllocateZeroPool(StrSize(NewString));
1049 if (*CommandString == NULL) {
1050 return (EFI_OUT_OF_RESOURCES);
1051 }
1052 StrCpy(*CommandString, NewString);
1053 return (EFI_SUCCESS);
1054 }
1055
1056 /**
1057 Function allocates a new command line and replaces all instances of environment
1058 variable names that are correctly preset to their values.
1059
1060 If the return value is not NULL the memory must be caller freed.
1061
1062 @param[in] OriginalCommandLine The original command line
1063
1064 @retval NULL An error ocurred.
1065 @return The new command line with no environment variables present.
1066 **/
1067 CHAR16*
1068 EFIAPI
1069 ShellConvertVariables (
1070 IN CONST CHAR16 *OriginalCommandLine
1071 )
1072 {
1073 CONST CHAR16 *MasterEnvList;
1074 UINTN NewSize;
1075 CHAR16 *NewCommandLine1;
1076 CHAR16 *NewCommandLine2;
1077 CHAR16 *Temp;
1078 UINTN ItemSize;
1079 CHAR16 *ItemTemp;
1080 SCRIPT_FILE *CurrentScriptFile;
1081 ALIAS_LIST *AliasListNode;
1082
1083 ASSERT(OriginalCommandLine != NULL);
1084
1085 ItemSize = 0;
1086 NewSize = StrSize(OriginalCommandLine);
1087 CurrentScriptFile = ShellCommandGetCurrentScriptFile();
1088 Temp = NULL;
1089
1090 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1091
1092 //
1093 // calculate the size required for the post-conversion string...
1094 //
1095 if (CurrentScriptFile != NULL) {
1096 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1097 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1098 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1099 ){
1100 for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)
1101 ; Temp != NULL
1102 ; Temp = StrStr(Temp+1, AliasListNode->Alias)
1103 ){
1104 //
1105 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1106 //
1107 if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {
1108 NewSize += StrSize(AliasListNode->CommandString);
1109 }
1110 }
1111 }
1112 }
1113
1114 for (MasterEnvList = EfiShellGetEnv(NULL)
1115 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
1116 ; MasterEnvList += StrLen(MasterEnvList) + 1
1117 ){
1118 if (StrSize(MasterEnvList) > ItemSize) {
1119 ItemSize = StrSize(MasterEnvList);
1120 }
1121 for (Temp = StrStr(OriginalCommandLine, MasterEnvList)
1122 ; Temp != NULL
1123 ; Temp = StrStr(Temp+1, MasterEnvList)
1124 ){
1125 //
1126 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1127 //
1128 if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&
1129 ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {
1130 NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));
1131 }
1132 }
1133 }
1134
1135 //
1136 // Quick out if none were found...
1137 //
1138 if (NewSize == StrSize(OriginalCommandLine)) {
1139 ASSERT(Temp == NULL);
1140 Temp = StrnCatGrow(&Temp, NULL, OriginalCommandLine, 0);
1141 return (Temp);
1142 }
1143
1144 //
1145 // now do the replacements...
1146 //
1147 NewCommandLine1 = AllocateZeroPool(NewSize);
1148 NewCommandLine2 = AllocateZeroPool(NewSize);
1149 ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));
1150 if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {
1151 SHELL_FREE_NON_NULL(NewCommandLine1);
1152 SHELL_FREE_NON_NULL(NewCommandLine2);
1153 SHELL_FREE_NON_NULL(ItemTemp);
1154 return (NULL);
1155 }
1156 StrCpy(NewCommandLine1, OriginalCommandLine);
1157 for (MasterEnvList = EfiShellGetEnv(NULL)
1158 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
1159 ; MasterEnvList += StrLen(MasterEnvList) + 1
1160 ){
1161 StrCpy(ItemTemp, L"%");
1162 StrCat(ItemTemp, MasterEnvList);
1163 StrCat(ItemTemp, L"%");
1164 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
1165 StrCpy(NewCommandLine1, NewCommandLine2);
1166 }
1167 if (CurrentScriptFile != NULL) {
1168 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1169 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1170 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1171 ){
1172 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
1173 StrCpy(NewCommandLine1, NewCommandLine2);
1174 }
1175 }
1176
1177 FreePool(NewCommandLine2);
1178 FreePool(ItemTemp);
1179
1180 return (NewCommandLine1);
1181 }
1182
1183 /**
1184 Internal function to run a command line with pipe usage.
1185
1186 @param[in] CmdLine The pointer to the command line.
1187 @param[in] StdIn The pointer to the Standard input.
1188 @param[in] StdOut The pointer to the Standard output.
1189
1190 @retval EFI_SUCCESS The split command is executed successfully.
1191 @retval other Some error occurs when executing the split command.
1192 **/
1193 EFI_STATUS
1194 EFIAPI
1195 RunSplitCommand(
1196 IN CONST CHAR16 *CmdLine,
1197 IN SHELL_FILE_HANDLE *StdIn,
1198 IN SHELL_FILE_HANDLE *StdOut
1199 )
1200 {
1201 EFI_STATUS Status;
1202 CHAR16 *NextCommandLine;
1203 CHAR16 *OurCommandLine;
1204 UINTN Size1;
1205 UINTN Size2;
1206 SPLIT_LIST *Split;
1207 SHELL_FILE_HANDLE *TempFileHandle;
1208 BOOLEAN Unicode;
1209
1210 ASSERT(StdOut == NULL);
1211
1212 ASSERT(StrStr(CmdLine, L"|") != NULL);
1213
1214 Status = EFI_SUCCESS;
1215 NextCommandLine = NULL;
1216 OurCommandLine = NULL;
1217 Size1 = 0;
1218 Size2 = 0;
1219
1220 NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);
1221 OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine);
1222
1223 if (NextCommandLine == NULL || OurCommandLine == NULL) {
1224 SHELL_FREE_NON_NULL(OurCommandLine);
1225 SHELL_FREE_NON_NULL(NextCommandLine);
1226 return (EFI_OUT_OF_RESOURCES);
1227 } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {
1228 SHELL_FREE_NON_NULL(OurCommandLine);
1229 SHELL_FREE_NON_NULL(NextCommandLine);
1230 return (EFI_INVALID_PARAMETER);
1231 } else if (NextCommandLine[0] != CHAR_NULL &&
1232 NextCommandLine[0] == L'a' &&
1233 NextCommandLine[1] == L' '
1234 ){
1235 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
1236 Unicode = FALSE;
1237 } else {
1238 Unicode = TRUE;
1239 }
1240
1241
1242 //
1243 // make a SPLIT_LIST item and add to list
1244 //
1245 Split = AllocateZeroPool(sizeof(SPLIT_LIST));
1246 ASSERT(Split != NULL);
1247 Split->SplitStdIn = StdIn;
1248 Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);
1249 ASSERT(Split->SplitStdOut != NULL);
1250 InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
1251
1252 Status = RunCommand(OurCommandLine);
1253
1254 //
1255 // move the output from the first to the in to the second.
1256 //
1257 TempFileHandle = Split->SplitStdOut;
1258 if (Split->SplitStdIn == StdIn) {
1259 Split->SplitStdOut = NULL;
1260 } else {
1261 Split->SplitStdOut = Split->SplitStdIn;
1262 }
1263 Split->SplitStdIn = TempFileHandle;
1264 ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
1265
1266 if (!EFI_ERROR(Status)) {
1267 Status = RunCommand(NextCommandLine);
1268 }
1269
1270 //
1271 // remove the top level from the ScriptList
1272 //
1273 ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);
1274 RemoveEntryList(&Split->Link);
1275
1276 //
1277 // Note that the original StdIn is now the StdOut...
1278 //
1279 if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {
1280 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));
1281 }
1282 if (Split->SplitStdIn != NULL) {
1283 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));
1284 }
1285
1286 FreePool(Split);
1287 FreePool(NextCommandLine);
1288 FreePool(OurCommandLine);
1289
1290 return (Status);
1291 }
1292
1293 /**
1294 Function will process and run a command line.
1295
1296 This will determine if the command line represents an internal shell
1297 command or dispatch an external application.
1298
1299 @param[in] CmdLine The command line to parse.
1300
1301 @retval EFI_SUCCESS The command was completed.
1302 @retval EFI_ABORTED The command's operation was aborted.
1303 **/
1304 EFI_STATUS
1305 EFIAPI
1306 RunCommand(
1307 IN CONST CHAR16 *CmdLine
1308 )
1309 {
1310 EFI_STATUS Status;
1311 CHAR16 *CommandName;
1312 SHELL_STATUS ShellStatus;
1313 UINTN Argc;
1314 CHAR16 **Argv;
1315 BOOLEAN LastError;
1316 CHAR16 LeString[11];
1317 CHAR16 *PostAliasCmdLine;
1318 UINTN PostAliasSize;
1319 CHAR16 *PostVariableCmdLine;
1320 CHAR16 *CommandWithPath;
1321 CONST EFI_DEVICE_PATH_PROTOCOL *DevPath;
1322 CONST CHAR16 *TempLocation;
1323 CONST CHAR16 *TempLocation2;
1324 SHELL_FILE_HANDLE OriginalStdIn;
1325 SHELL_FILE_HANDLE OriginalStdOut;
1326 SHELL_FILE_HANDLE OriginalStdErr;
1327 SYSTEM_TABLE_INFO OriginalSystemTableInfo;
1328 CHAR16 *TempLocation3;
1329 UINTN Count;
1330 UINTN Count2;
1331 CHAR16 *CleanOriginal;
1332 SPLIT_LIST *Split;
1333
1334 ASSERT(CmdLine != NULL);
1335 if (StrLen(CmdLine) == 0) {
1336 return (EFI_SUCCESS);
1337 }
1338
1339 CommandName = NULL;
1340 PostVariableCmdLine = NULL;
1341 PostAliasCmdLine = NULL;
1342 CommandWithPath = NULL;
1343 DevPath = NULL;
1344 Status = EFI_SUCCESS;
1345 CleanOriginal = NULL;
1346 Split = NULL;
1347
1348 CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
1349 if (CleanOriginal == NULL) {
1350 return (EFI_OUT_OF_RESOURCES);
1351 }
1352 while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {
1353 CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;
1354 }
1355 while (CleanOriginal[0] == L' ') {
1356 CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));
1357 }
1358
1359 CommandName = NULL;
1360 if (StrStr(CleanOriginal, L" ") == NULL){
1361 StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);
1362 } else {
1363 StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);
1364 }
1365
1366 ASSERT(PostAliasCmdLine == NULL);
1367 if (!ShellCommandIsCommandOnList(CommandName)) {
1368 //
1369 // Convert via alias
1370 //
1371 Status = ShellConvertAlias(&CommandName);
1372 PostAliasSize = 0;
1373 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);
1374 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);
1375 ASSERT_EFI_ERROR(Status);
1376 } else {
1377 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);
1378 }
1379
1380 if (CleanOriginal != NULL) {
1381 FreePool(CleanOriginal);
1382 CleanOriginal = NULL;
1383 }
1384
1385 if (CommandName != NULL) {
1386 FreePool(CommandName);
1387 CommandName = NULL;
1388 }
1389
1390 PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);
1391
1392 //
1393 // we can now free the modified by alias command line
1394 //
1395 if (PostAliasCmdLine != NULL) {
1396 FreePool(PostAliasCmdLine);
1397 PostAliasCmdLine = NULL;
1398 }
1399
1400 if (PostVariableCmdLine == NULL) {
1401 return (EFI_OUT_OF_RESOURCES);
1402 }
1403
1404 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
1405 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
1406 }
1407 while (PostVariableCmdLine[0] == L' ') {
1408 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
1409 }
1410
1411 //
1412 // We dont do normal processing with a split command line (output from one command input to another)
1413 //
1414 TempLocation3 = NULL;
1415 if (StrStr(PostVariableCmdLine, L"|") != NULL) {
1416 for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {
1417 if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') {
1418 TempLocation3++;
1419 } else if (*TempLocation3 == L'|') {
1420 break;
1421 }
1422 }
1423 }
1424 if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {
1425 //
1426 // are we in an existing split???
1427 //
1428 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
1429 Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
1430 }
1431
1432 if (Split == NULL) {
1433 Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);
1434 } else {
1435 Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);
1436 }
1437 if (EFI_ERROR(Status)) {
1438 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, PostVariableCmdLine);
1439 }
1440 } else {
1441
1442 //
1443 // If this is a mapped drive change handle that...
1444 //
1445 if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {
1446 Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);
1447 if (EFI_ERROR(Status)) {
1448 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);
1449 }
1450 FreePool(PostVariableCmdLine);
1451 return (Status);
1452 }
1453
1454 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1455 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1456
1457
1458
1459 Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
1460 if (EFI_ERROR(Status)) {
1461 if (Status == EFI_NOT_FOUND) {
1462 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);
1463 } else {
1464 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);
1465 }
1466 } else {
1467 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
1468 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
1469 }
1470 while (PostVariableCmdLine[0] == L' ') {
1471 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
1472 }
1473
1474 //
1475 // get the argc and argv updated for internal commands
1476 //
1477 Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);
1478 ASSERT_EFI_ERROR(Status);
1479
1480 for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {
1481 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]
1482 || (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)
1483 ) {
1484 //
1485 // We need to redo the arguments since a parameter was -?
1486 // move them all down 1 to the end, then up one then replace the first with help
1487 //
1488 FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);
1489 ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;
1490 for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {
1491 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];
1492 }
1493 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;
1494 for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {
1495 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];
1496 }
1497 ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;
1498 ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);
1499 break;
1500 }
1501 }
1502
1503 //
1504 // command or file?
1505 //
1506 if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
1507 //
1508 // Run the command (which was converted if it was an alias)
1509 //
1510 if (!EFI_ERROR(Status)) {
1511 Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);
1512 ASSERT_EFI_ERROR(Status);
1513 UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", ShellStatus);
1514 DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););
1515 if (LastError) {
1516 InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);
1517 }
1518 //
1519 // Pass thru the exitcode from the app.
1520 //
1521 if (ShellCommandGetExit()) {
1522 Status = ShellStatus;
1523 } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
1524 Status = EFI_ABORTED;
1525 }
1526 }
1527 } else {
1528 //
1529 // run an external file (or script)
1530 //
1531 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {
1532 ASSERT (CommandWithPath == NULL);
1533 if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {
1534 CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
1535 }
1536 }
1537 if (CommandWithPath == NULL) {
1538 CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);
1539 }
1540 if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {
1541 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);
1542 } else {
1543 //
1544 // Check if it's a NSH (script) file.
1545 //
1546 TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;
1547 TempLocation2 = mScriptExtension;
1548 if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {
1549 Status = RunScriptFile (CommandWithPath);
1550 } else {
1551 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
1552 ASSERT(DevPath != NULL);
1553 Status = InternalShellExecuteDevicePath(
1554 &gImageHandle,
1555 DevPath,
1556 PostVariableCmdLine,
1557 NULL,
1558 NULL
1559 );
1560 }
1561 }
1562 }
1563 CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
1564
1565 RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);
1566
1567 RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
1568 }
1569 if (CommandName != NULL) {
1570 if (ShellCommandGetCurrentScriptFile() != NULL && !IsScriptOnlyCommand(CommandName)) {
1571 //
1572 // if this is NOT a scipt only command return success so the script won't quit.
1573 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1574 //
1575 Status = EFI_SUCCESS;
1576 }
1577 }
1578 }
1579
1580 SHELL_FREE_NON_NULL(CommandName);
1581 SHELL_FREE_NON_NULL(CommandWithPath);
1582 SHELL_FREE_NON_NULL(PostVariableCmdLine);
1583
1584 return (Status);
1585 }
1586
1587 STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
1588 /**
1589 Function determins if the CommandName COULD be a valid command. It does not determine whether
1590 this is a valid command. It only checks for invalid characters.
1591
1592 @param[in] CommandName The name to check
1593
1594 @retval TRUE CommandName could be a command name
1595 @retval FALSE CommandName could not be a valid command name
1596 **/
1597 BOOLEAN
1598 EFIAPI
1599 IsValidCommandName(
1600 IN CONST CHAR16 *CommandName
1601 )
1602 {
1603 UINTN Count;
1604 if (CommandName == NULL) {
1605 ASSERT(FALSE);
1606 return (FALSE);
1607 }
1608 for ( Count = 0
1609 ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
1610 ; Count++
1611 ){
1612 if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
1613 return (FALSE);
1614 }
1615 }
1616 return (TRUE);
1617 }
1618
1619 /**
1620 Function to process a NSH script file via SHELL_FILE_HANDLE.
1621
1622 @param[in] Handle The handle to the already opened file.
1623 @param[in] Name The name of the script file.
1624
1625 @retval EFI_SUCCESS the script completed sucessfully
1626 **/
1627 EFI_STATUS
1628 EFIAPI
1629 RunScriptFileHandle (
1630 IN SHELL_FILE_HANDLE Handle,
1631 IN CONST CHAR16 *Name
1632 )
1633 {
1634 EFI_STATUS Status;
1635 SCRIPT_FILE *NewScriptFile;
1636 UINTN LoopVar;
1637 CHAR16 *CommandLine;
1638 CHAR16 *CommandLine2;
1639 CHAR16 *CommandLine3;
1640 SCRIPT_COMMAND_LIST *LastCommand;
1641 BOOLEAN Ascii;
1642 BOOLEAN PreScriptEchoState;
1643 BOOLEAN PreCommandEchoState;
1644 CONST CHAR16 *CurDir;
1645 UINTN LineCount;
1646 CHAR16 LeString[50];
1647
1648 ASSERT(!ShellCommandGetScriptExit());
1649
1650 PreScriptEchoState = ShellCommandGetEchoState();
1651
1652 NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
1653 if (NewScriptFile == NULL) {
1654 return (EFI_OUT_OF_RESOURCES);
1655 }
1656
1657 //
1658 // Set up the name
1659 //
1660 ASSERT(NewScriptFile->ScriptName == NULL);
1661 NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
1662 if (NewScriptFile->ScriptName == NULL) {
1663 DeleteScriptFileStruct(NewScriptFile);
1664 return (EFI_OUT_OF_RESOURCES);
1665 }
1666
1667 //
1668 // Save the parameters (used to replace %0 to %9 later on)
1669 //
1670 NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
1671 if (NewScriptFile->Argc != 0) {
1672 NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));
1673 if (NewScriptFile->Argv == NULL) {
1674 DeleteScriptFileStruct(NewScriptFile);
1675 return (EFI_OUT_OF_RESOURCES);
1676 }
1677 for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
1678 ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
1679 NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
1680 if (NewScriptFile->Argv[LoopVar] == NULL) {
1681 DeleteScriptFileStruct(NewScriptFile);
1682 return (EFI_OUT_OF_RESOURCES);
1683 }
1684 }
1685 } else {
1686 NewScriptFile->Argv = NULL;
1687 }
1688
1689 InitializeListHead(&NewScriptFile->CommandList);
1690 InitializeListHead(&NewScriptFile->SubstList);
1691
1692 //
1693 // Now build the list of all script commands.
1694 //
1695 LineCount = 0;
1696 while(!ShellFileHandleEof(Handle)) {
1697 CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
1698 LineCount++;
1699 if (CommandLine == NULL || StrLen(CommandLine) == 0) {
1700 continue;
1701 }
1702 NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
1703 if (NewScriptFile->CurrentCommand == NULL) {
1704 DeleteScriptFileStruct(NewScriptFile);
1705 return (EFI_OUT_OF_RESOURCES);
1706 }
1707
1708 NewScriptFile->CurrentCommand->Cl = CommandLine;
1709 NewScriptFile->CurrentCommand->Data = NULL;
1710 NewScriptFile->CurrentCommand->Line = LineCount;
1711
1712 InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1713 }
1714
1715 //
1716 // Add this as the topmost script file
1717 //
1718 ShellCommandSetNewScript (NewScriptFile);
1719
1720 //
1721 // Now enumerate through the commands and run each one.
1722 //
1723 CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
1724 if (CommandLine == NULL) {
1725 DeleteScriptFileStruct(NewScriptFile);
1726 return (EFI_OUT_OF_RESOURCES);
1727 }
1728 CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
1729 if (CommandLine2 == NULL) {
1730 FreePool(CommandLine);
1731 DeleteScriptFileStruct(NewScriptFile);
1732 return (EFI_OUT_OF_RESOURCES);
1733 }
1734
1735 for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
1736 ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
1737 ; // conditional increment in the body of the loop
1738 ){
1739 ASSERT(CommandLine2 != NULL);
1740 StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);
1741
1742 //
1743 // NULL out comments
1744 //
1745 for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
1746 if (*CommandLine3 == L'^') {
1747 if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {
1748 CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
1749 }
1750 } else if (*CommandLine3 == L'#') {
1751 *CommandLine3 = CHAR_NULL;
1752 }
1753 }
1754
1755 if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
1756 //
1757 // Due to variability in starting the find and replace action we need to have both buffers the same.
1758 //
1759 StrCpy(CommandLine, CommandLine2);
1760
1761 //
1762 // Remove the %0 to %9 from the command line (if we have some arguments)
1763 //
1764 if (NewScriptFile->Argv != NULL) {
1765 switch (NewScriptFile->Argc) {
1766 default:
1767 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);
1768 ASSERT_EFI_ERROR(Status);
1769 case 9:
1770 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);
1771 ASSERT_EFI_ERROR(Status);
1772 case 8:
1773 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);
1774 ASSERT_EFI_ERROR(Status);
1775 case 7:
1776 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);
1777 ASSERT_EFI_ERROR(Status);
1778 case 6:
1779 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);
1780 ASSERT_EFI_ERROR(Status);
1781 case 5:
1782 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);
1783 ASSERT_EFI_ERROR(Status);
1784 case 4:
1785 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);
1786 ASSERT_EFI_ERROR(Status);
1787 case 3:
1788 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);
1789 ASSERT_EFI_ERROR(Status);
1790 case 2:
1791 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);
1792 ASSERT_EFI_ERROR(Status);
1793 case 1:
1794 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);
1795 ASSERT_EFI_ERROR(Status);
1796 break;
1797 case 0:
1798 break;
1799 }
1800 }
1801 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);
1802 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);
1803 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);
1804 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);
1805 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);
1806 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);
1807 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);
1808 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);
1809 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);
1810
1811 StrCpy(CommandLine2, CommandLine);
1812
1813 LastCommand = NewScriptFile->CurrentCommand;
1814
1815 for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);
1816
1817 if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {
1818 //
1819 // This line is a goto target / label
1820 //
1821 } else {
1822 if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
1823 if (CommandLine3[0] == L'@') {
1824 //
1825 // We need to save the current echo state
1826 // and disable echo for just this command.
1827 //
1828 PreCommandEchoState = ShellCommandGetEchoState();
1829 ShellCommandSetEchoState(FALSE);
1830 Status = RunCommand(CommandLine3+1);
1831
1832 //
1833 // Now restore the pre-'@' echo state.
1834 //
1835 ShellCommandSetEchoState(PreCommandEchoState);
1836 } else {
1837 if (ShellCommandGetEchoState()) {
1838 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
1839 if (CurDir != NULL && StrLen(CurDir) > 1) {
1840 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
1841 } else {
1842 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
1843 }
1844 ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
1845 }
1846 Status = RunCommand(CommandLine3);
1847 }
1848 }
1849
1850 if (ShellCommandGetScriptExit()) {
1851 UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%Lx", ShellCommandGetExitCode());
1852 DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););
1853 InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);
1854
1855 ShellCommandRegisterExit(FALSE, 0);
1856 Status = EFI_SUCCESS;
1857 break;
1858 }
1859 if (EFI_ERROR(Status)) {
1860 break;
1861 }
1862 if (ShellCommandGetExit()) {
1863 break;
1864 }
1865 }
1866 //
1867 // If that commend did not update the CurrentCommand then we need to advance it...
1868 //
1869 if (LastCommand == NewScriptFile->CurrentCommand) {
1870 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1871 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
1872 NewScriptFile->CurrentCommand->Reset = TRUE;
1873 }
1874 }
1875 } else {
1876 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1877 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
1878 NewScriptFile->CurrentCommand->Reset = TRUE;
1879 }
1880 }
1881 }
1882
1883
1884 FreePool(CommandLine);
1885 FreePool(CommandLine2);
1886 ShellCommandSetNewScript (NULL);
1887
1888 //
1889 // Only if this was the last script reset the state.
1890 //
1891 if (ShellCommandGetCurrentScriptFile()==NULL) {
1892 ShellCommandSetEchoState(PreScriptEchoState);
1893 }
1894 return (EFI_SUCCESS);
1895 }
1896
1897 /**
1898 Function to process a NSH script file.
1899
1900 @param[in] ScriptPath Pointer to the script file name (including file system path).
1901
1902 @retval EFI_SUCCESS the script completed sucessfully
1903 **/
1904 EFI_STATUS
1905 EFIAPI
1906 RunScriptFile (
1907 IN CONST CHAR16 *ScriptPath
1908 )
1909 {
1910 EFI_STATUS Status;
1911 SHELL_FILE_HANDLE FileHandle;
1912
1913 if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
1914 return (EFI_INVALID_PARAMETER);
1915 }
1916
1917 Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
1918 if (EFI_ERROR(Status)) {
1919 return (Status);
1920 }
1921
1922 Status = RunScriptFileHandle(FileHandle, ScriptPath);
1923
1924 ShellCloseFile(&FileHandle);
1925
1926 return (Status);
1927 }