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