/*++ Copyright (c) 1999 Intel Corporation Module Name: for.c Abstract: Internal Shell cmd "for" & "endfor" Revision History --*/ #include "shelle.h" /* * Datatypes */ #define FOR_LOOP_INFO_SIGNATURE EFI_SIGNATURE_32('f','l','i','s') typedef struct { UINTN Signature; LIST_ENTRY Link; UINT64 LoopFilePos; CHAR16 *IndexVarName; LIST_ENTRY IndexValueList; } FOR_LOOP_INFO; #define FOR_LOOP_INDEXVAL_SIGNATURE EFI_SIGNATURE_32('f','l','v','s') typedef struct { UINTN Signature; LIST_ENTRY Link; CHAR16 *Value; } FOR_LOOP_INDEXVAL; /* * Statics */ STATIC LIST_ENTRY ForLoopInfoStack; STATIC UINTN NumActiveForLoops; VOID DumpForLoopInfoStack(VOID) { LIST_ENTRY *InfoLink; LIST_ENTRY *IndexLink; FOR_LOOP_INFO *LoopInfo; FOR_LOOP_INDEXVAL *LoopIndexVal; Print( L"FOR LOOP INFO STACK DUMP\n" ); for ( InfoLink = ForLoopInfoStack.Flink; InfoLink!=&ForLoopInfoStack; InfoLink=InfoLink->Flink) { LoopInfo = CR(InfoLink, FOR_LOOP_INFO, Link, FOR_LOOP_INFO_SIGNATURE); if ( LoopInfo ) { Print( L" LoopFilePos 0x%X\n", LoopInfo->LoopFilePos ); Print( L" IndexVarName %s (0x%X)\n", LoopInfo->IndexVarName, LoopInfo->IndexVarName ); for ( IndexLink = LoopInfo->IndexValueList.Flink; IndexLink!=&LoopInfo->IndexValueList; IndexLink=IndexLink->Flink ) { LoopIndexVal = CR(IndexLink, FOR_LOOP_INDEXVAL, Link, FOR_LOOP_INDEXVAL_SIGNATURE); if ( LoopIndexVal ) { if ( LoopIndexVal->Value ) { Print( L" Loop index value %s\n", LoopIndexVal->Value ); } else { Print( L" Loop index value is NULL\n" ); } } else { Print( L" Loop index value structure pointer is NULL\n" ); } } } else { Print( L" LoopInfo NULL\n" ); } } return; } /*/////////////////////////////////////////////////////////////////////// Function Name: SEnvInitForLoopInfo Description: Initialize data structures used in or loop management. */ VOID SEnvInitForLoopInfo ( VOID ) { InitializeListHead( &ForLoopInfoStack ); NumActiveForLoops = 0; return; } /*/////////////////////////////////////////////////////////////////////// Function Name: SEnvSubstituteForLoopIndex Description: Builtin shell command "for" for conditional execution in script files. */ EFI_STATUS SEnvSubstituteForLoopIndex( IN CHAR16 *Str, OUT CHAR16 **Val ) { LIST_ENTRY *InfoLink = NULL; LIST_ENTRY *IndexLink = NULL; FOR_LOOP_INFO *LoopInfo = NULL; FOR_LOOP_INDEXVAL *LoopIndexVal = NULL; EFI_STATUS Status = EFI_SUCCESS; /* * Check if Str is a forloop index variable name on the forloop info stack * If it is, return the current value * Otherwise, just return the string. */ if ( Str[0] != L'%' || !IsWhiteSpace(Str[2]) ) { Status = EFI_INVALID_PARAMETER; goto Done; } /* * We may have nested for loops, so we have to search through the variables for * each for loop on the stack to see if we can match the variable name. */ for ( InfoLink = ForLoopInfoStack.Flink; InfoLink!=&ForLoopInfoStack; InfoLink=InfoLink->Flink) { LoopInfo = CR(InfoLink, FOR_LOOP_INFO, Link, FOR_LOOP_INFO_SIGNATURE); if ( LoopInfo ) { if ( Str[1] == LoopInfo->IndexVarName[0] ) { /* Found a match */ IndexLink = LoopInfo->IndexValueList.Flink; LoopIndexVal = CR(IndexLink, FOR_LOOP_INDEXVAL, Link, FOR_LOOP_INDEXVAL_SIGNATURE); if ( LoopIndexVal && LoopIndexVal->Value ) { *Val = LoopIndexVal->Value; Status = EFI_SUCCESS; goto Done; } else { Status = EFI_INVALID_PARAMETER; goto Done; } } } } *Val = NULL; Done: return Status; } /*/////////////////////////////////////////////////////////////////////// Function Name: SEnvCmdFor Description: Builtin shell command "for" for conditional execution in script files. */ EFI_STATUS SEnvCmdFor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { CHAR16 **Argv; UINTN Argc = 0; UINTN Index = 0; EFI_STATUS Status = EFI_SUCCESS; UINTN i = 0; LIST_ENTRY FileList; LIST_ENTRY *Link = NULL; SHELL_FILE_ARG *Arg = NULL; FOR_LOOP_INFO *NewInfo = NULL; FOR_LOOP_INDEXVAL *NewIndexVal = NULL; InitializeShellApplication (ImageHandle, SystemTable); Argv = SI->Argv; Argc = SI->Argc; InitializeListHead( &FileList ); if ( !SEnvBatchIsActive() ) { Print( L"Error: FOR command only supported in script files\n" ); Status = EFI_UNSUPPORTED; goto Done; } /* * First, parse the command line arguments * * for % in */ if ( Argc < 4 || (StriCmp( Argv[2], L"in" ) != 0) || !(StrLen(Argv[1]) == 1 && IsAlpha(Argv[1][0]) ) ) { Print( L"Argc %d, Argv[2] %s, StrLen(Argv[1]) %d, Argv[1][0] %c\n", Argc, Argv[2], StrLen(Argv[1]), Argv[1][0] ); Status = EFI_INVALID_PARAMETER; goto Done; } /* * Allocate a new forloop info structure for this for loop, and * puch it on the for loop info stack. */ NewInfo = AllocateZeroPool( sizeof( FOR_LOOP_INFO ) ); if ( !NewInfo ) { Status = EFI_OUT_OF_RESOURCES; goto Done; } NewInfo->Signature = FOR_LOOP_INFO_SIGNATURE; InsertHeadList( &ForLoopInfoStack, &NewInfo->Link ); /* * Save the current script file position and the index variable name on * the for-loop info stack. Increment the active-for-loop counter. */ SEnvBatchGetFilePos( &NewInfo->LoopFilePos ); InitializeListHead( &NewInfo->IndexValueList ); NumActiveForLoops++; NewInfo->IndexVarName = StrDuplicate( Argv[1] ); /* * Put the set of index values in the index value list for this for loop */ for ( i=3; iFlink) { Arg = CR(Link, SHELL_FILE_ARG, Link, SHELL_FILE_ARG_SIGNATURE); NewIndexVal = AllocateZeroPool( sizeof(FOR_LOOP_INDEXVAL) ); if ( !NewIndexVal ) { Status = EFI_OUT_OF_RESOURCES; goto Done; } NewIndexVal->Signature = FOR_LOOP_INDEXVAL_SIGNATURE; InsertTailList( &NewInfo->IndexValueList, &NewIndexVal->Link ); NewIndexVal->Value = AllocateZeroPool( StrSize(Arg->FileName) + sizeof(CHAR16) ); if ( !NewIndexVal->Value ) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpy( NewIndexVal->Value, Arg->FileName ); } /* * Free the file list that was allocated by ShellFileMetaArg */ ShellFreeFileList (&FileList); } /* * Return control to the batch processing loop until an ENDFOR is encountered */ Done: /* * Free the file list */ if ( !IsListEmpty( &FileList ) ) { ShellFreeFileList (&FileList); } return Status; } /*/////////////////////////////////////////////////////////////////////// Function Name: SEnvCmdEndfor Description: Builtin shell command "endfor". */ EFI_STATUS SEnvCmdEndfor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status = EFI_SUCCESS; LIST_ENTRY *InfoLink = NULL; LIST_ENTRY *IndexLink = NULL; FOR_LOOP_INFO *LoopInfo = NULL; FOR_LOOP_INDEXVAL *LoopIndexVal = NULL; InitializeShellApplication (ImageHandle, SystemTable); if ( !SEnvBatchIsActive() ) { Print( L"Error: ENDFOR command only supported in script files\n" ); Status = EFI_UNSUPPORTED; goto Done; } if ( NumActiveForLoops == 0 ) { Print( L"Error: ENDFOR with no corresponding FOR\n" ); Status = EFI_INVALID_PARAMETER; goto Done; } /* * Discard the index value for the just-completed iteration */ /* Get a pointer to the FOR_LOOP_INFO structure at the top of the stack (list) */ InfoLink = ForLoopInfoStack.Flink; LoopInfo = CR(InfoLink, FOR_LOOP_INFO, Link, FOR_LOOP_INFO_SIGNATURE); if ( LoopInfo ) { /* Get a pointer to the FOR_LOOP_INDEXVAL structure at the front of the list */ IndexLink = LoopInfo->IndexValueList.Flink; LoopIndexVal = CR(IndexLink, FOR_LOOP_INDEXVAL, Link, FOR_LOOP_INDEXVAL_SIGNATURE); if ( LoopIndexVal ) { /* Free the string containing the index value */ if ( LoopIndexVal->Value ) { FreePool( LoopIndexVal->Value ); LoopIndexVal->Value = NULL; } /* Remove the used index value structure from the list and free it */ RemoveEntryList( &LoopIndexVal->Link ); FreePool( LoopIndexVal ); LoopIndexVal = NULL; /* * If there is another value, then jump back to top of loop, * otherwise, exit this FOR loop & pop the FOR loop info stack. */ if ( !IsListEmpty( &LoopInfo->IndexValueList ) ) { /* * Set script file position back to top of this loop */ Status = SEnvBatchSetFilePos( LoopInfo->LoopFilePos ); if ( EFI_ERROR(Status) ) { goto Done; } } else { if ( LoopInfo->IndexVarName ) { FreePool( LoopInfo->IndexVarName ); LoopInfo->IndexVarName = NULL; } /* * Pop the stack and free the popped for loop info struct */ RemoveEntryList( &LoopInfo->Link ); if ( LoopInfo->IndexVarName ) { FreePool( LoopInfo->IndexVarName ); } FreePool( LoopInfo ); LoopInfo = NULL; NumActiveForLoops--; } } } Done: return EFI_SUCCESS; }