我们在 EDK2 的代码中能看到 Shell下部分命令的代码,这里介绍如何把这样的代码提取出来做成能够独立编译和运行的程序。简单起见,以 Stall 命令和 MV 命令为例。
经过试验,这些命令中使用到的大部分函数都可以在 ShellLib.h 中找到,我们要做的只是把这个文件copy一份到我们程序下面。
最后修改之后的程序如下:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include "ShellLib.h"
#define ASSERT(Expression)
extern EFI_BOOT_SERVICES *gBS;
extern EFI_SYSTEM_TABLE *gST;
extern EFI_RUNTIME_SERVICES *gRT;
CONST CHAR16 STR_GEN_PROBLEM[] = L"Error. The argument '%s' is incorrect.\r\n";
CONST CHAR16 STR_GEN_TOO_FEW[] = L"Error. Too few arguments specified.\r\n";
CONST CHAR16 STR_GEN_TOO_MANY[]= L"Error. Too many arguments specified.\r\n";
CONST CHAR16 STR_GEN_PROBLEM_VAL[] = L"Error. The argument '%s' has incorrect value.\r\n";
CONST CHAR16 STR_STALL_FAILED[] = L"Error. BootService Stall() failed with %r.\r\n";
int
EFIAPI
main (
IN int Argc,
IN CHAR16 **Argv
)
{
EFI_STATUS Status;
LIST_ENTRY *Package;
CHAR16 *ProblemParam;
SHELL_STATUS ShellStatus;
UINT64 Intermediate;
//
// parse the command line
//
Status = ShellCommandLineParse (EmptyParamList, &Package, &ProblemParam, TRUE);
if (EFI_ERROR(Status)) {
if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
Print(STR_GEN_PROBLEM,ProblemParam);
FreePool(ProblemParam);
ShellStatus = SHELL_INVALID_PARAMETER;
} else {
ASSERT(FALSE);
}
} else {
if (ShellCommandLineGetRawValue(Package, 2) != NULL) {
Print(STR_GEN_TOO_MANY);
ShellStatus = SHELL_INVALID_PARAMETER;
} else if (ShellCommandLineGetRawValue(Package, 1) == NULL) {
Print(STR_GEN_TOO_FEW);
ShellStatus = SHELL_INVALID_PARAMETER;
} else {
Status = ShellConvertStringToUint64(
ShellCommandLineGetRawValue(Package, 1), &Intermediate, FALSE, FALSE);
if (EFI_ERROR(Status) || ((UINT64)(UINTN)(Intermediate)) != Intermediate) {
Print(STR_GEN_PROBLEM_VAL, ShellCommandLineGetRawValue(Package, 1));
ShellStatus = SHELL_INVALID_PARAMETER;
} else {
Status = gBS->Stall((UINTN)Intermediate);
if (EFI_ERROR(Status)) {
Print(STR_STALL_FAILED, Status);
ShellStatus = SHELL_DEVICE_ERROR;
}
}
}
ShellCommandLineFreeVarList (Package);
}
return EFI_SUCCESS;
}
运行结果:

完整代码下载(特别注意,涉及到时钟的程序在NT32模拟环境中和实际环境中存在很大差别,不要在实际环境中使用为模拟环境编译的EFI文件)。
zStall
总结一下,如果想把一个命令改造为独立的程序,需要做下面的事情:
1. 增加 #define ASSERT(Expression) 这个定义,上面代码为了简单,我只是定义它为空
2. 拷贝 ShellLib.h 到你的代码目录下,然后用 “”直接使用
3. 改造程序中定义的字符串,这些字符串都是定义在 UNI 文件中。如果你没有多语言的需要,可以像我这样在代码中重新定义一次
4. 将所有的 ShellPrintHiiEx 都修改为 Print
下面再用 MV 命令的代码练习一下
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/UnicodeCollation.h>
#include "ShellLib.h"
#define ASSERT(Expression)
extern EFI_BOOT_SERVICES *gBS;
extern EFI_SYSTEM_TABLE *gST;
extern EFI_RUNTIME_SERVICES *gRT;
CONST CHAR16 STR_GEN_PROBLEM[] = L"Error. The argument '%s' is incorrect.\r\n";
CONST CHAR16 STR_GEN_TOO_MANY[]= L"Error. Too many arguments specified.\r\n";
CONST CHAR16 STR_GEN_TOO_FEW[] = L"Error. Too few arguments specified.\r\n";
CONST CHAR16 STR_GEN_NO_CWD[] = L"Error. No current directory is specified.\r\n";
CONST CHAR16 STR_GEN_FILE_NF[] = L"Error. File '%s' was not found.\r\n";
CONST CHAR16 STR_GEN_ERR_FILE[]= L"Error. File '%s' error: %r\r\n";
CONST CHAR16 STR_MV_INV_SUB[] = L"Error. Cannot move a directory into itself or its subdirectory.\r\n";
CONST CHAR16 STR_MV_INV_RO[] = L"Error. Cannot move a read-only File or Directory.\r\n";
CONST CHAR16 STR_MV_INV_CWD[] = L"Error. Cannot move current working directory or its subdirectory.\r\n";
CONST CHAR16 STR_MV_INV_FS[] = L"Error. Cannot move between file systems.\r\n";
CONST CHAR16 STR_GEN_NO_MEM[] = L"Error. Memory is not available.\r\n";
CONST CHAR16 STR_GEN_ERR_UK[] = L"Error: %r\r\n";
CONST CHAR16 STR_MV_OUTPUT[] = L"Moving %s -> %s\r\n";
CONST CHAR16 STR_GEN_RES_OK[] = L"- [ok]\r\n";
CONST CHAR16 STR_GEN_MARG_ERROR[] = L"Error. The destination '%s' is ambigious.\r\n";
CONST CHAR16 STR_GEN_FILE_ERROR[] = L"Error. The destination is an existant file '%s'.\r\n";
//copy from \ShellPkg\Library\BasePathLib\BasePathLib.c
/**
Removes the last directory or file entry in a path by changing the last
L'\' to a CHAR_NULL.
@param[in, out] Path The pointer to the path to modify.
@retval FALSE Nothing was found to remove.
@retval TRUE A directory or file was removed.
**/
BOOLEAN
EFIAPI
PathRemoveLastItem(
IN OUT CHAR16 *Path
)
{
CHAR16 *Walker;
CHAR16 *LastSlash;
//
// get directory name from path... ('chop' off extra)
//
for ( Walker = Path, LastSlash = NULL
; Walker != NULL && *Walker != CHAR_NULL
; Walker++
){
if (*Walker == L'\\' && *(Walker + 1) != CHAR_NULL) {
LastSlash = Walker+1;
}
}
if (LastSlash != NULL) {
*LastSlash = CHAR_NULL;
return (TRUE);
}
return (FALSE);
}
/**
Function to take a destination path that might contain wildcards and verify
that there is only a single possible target (IE we cant have wildcards that
have 2 possible destination).
if the result is sucessful the caller must free *DestPathPointer.
@param[in] DestDir The original path to the destination.
@param[in, out] DestPathPointer A pointer to the callee allocated final path.
@param[in] Cwd A pointer to the current working directory.
@retval SHELL_INVALID_PARAMETER The DestDir could not be resolved to a location.
@retval SHELL_INVALID_PARAMETER The DestDir could be resolved to more than 1 location.
@retval SHELL_INVALID_PARAMETER Cwd is required and is NULL.
@retval SHELL_SUCCESS The operation was sucessful.
**/
SHELL_STATUS
EFIAPI
GetDestinationLocation(
IN CONST CHAR16 *DestDir,
IN OUT CHAR16 **DestPathPointer,
IN CONST CHAR16 *Cwd
)
{
EFI_SHELL_FILE_INFO *DestList;
EFI_SHELL_FILE_INFO *Node;
CHAR16 *DestPath;
UINTN NewSize;
UINTN CurrentSize;
DestList = NULL;
DestPath = NULL;
if (StrStr(DestDir, L"\\") == DestDir) {
if (Cwd == NULL) {
return SHELL_INVALID_PARAMETER;
}
DestPath = AllocateZeroPool(StrSize(Cwd));
if (DestPath == NULL) {
return (SHELL_OUT_OF_RESOURCES);
}
StrCpy(DestPath, Cwd);
while (PathRemoveLastItem(DestPath)) ;
//
// Append DestDir beyond '\' which may be present
//
CurrentSize = StrSize(DestPath);
StrnCatGrow(&DestPath, &CurrentSize, &DestDir[1], 0);
*DestPathPointer = DestPath;
return (SHELL_SUCCESS);
}
//
// get the destination path
//
ShellOpenFileMetaArg((CHAR16*)DestDir, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE, &DestList);
if (DestList == NULL || IsListEmpty(&DestList->Link)) {
//
// Not existing... must be renaming
//
if (StrStr(DestDir, L":") == NULL) {
if (Cwd == NULL) {
ShellCloseFileMetaArg(&DestList);
return (SHELL_INVALID_PARAMETER);
}
NewSize = StrSize(Cwd);
NewSize += StrSize(DestDir);
DestPath = AllocateZeroPool(NewSize);
if (DestPath == NULL) {
ShellCloseFileMetaArg(&DestList);
return (SHELL_OUT_OF_RESOURCES);
}
StrCpy(DestPath, Cwd);
if (DestPath[StrLen(DestPath)-1] != L'\\' && DestDir[0] != L'\\') {
StrCat(DestPath, L"\\");
} else if (DestPath[StrLen(DestPath)-1] == L'\\' && DestDir[0] == L'\\') {
((CHAR16*)DestPath)[StrLen(DestPath)-1] = CHAR_NULL;
}
StrCat(DestPath, DestDir);
} else {
ASSERT(DestPath == NULL);
DestPath = StrnCatGrow(&DestPath, NULL, DestDir, 0);
if (DestPath == NULL) {
ShellCloseFileMetaArg(&DestList);
return (SHELL_OUT_OF_RESOURCES);
}
}
} else {
Node = (EFI_SHELL_FILE_INFO*)GetFirstNode(&DestList->Link);
//
// Make sure there is only 1 node in the list.
//
if (!IsNodeAtEnd(&DestList->Link, &Node->Link)) {
ShellCloseFileMetaArg(&DestList);
Print(STR_GEN_MARG_ERROR, DestDir);
return (SHELL_INVALID_PARAMETER);
}
if (ShellIsDirectory(Node->FullName)==EFI_SUCCESS) {
DestPath = AllocateZeroPool(StrSize(Node->FullName)+sizeof(CHAR16));
if (DestPath == NULL) {
ShellCloseFileMetaArg(&DestList);
return (SHELL_OUT_OF_RESOURCES);
}
StrCpy(DestPath, Node->FullName);
StrCat(DestPath, L"\\");
} else {
//
// cant move onto another file.
//
ShellCloseFileMetaArg(&DestList);
Print(STR_GEN_FILE_ERROR,DestDir);
return (SHELL_INVALID_PARAMETER);
}
}
*DestPathPointer = DestPath;
ShellCloseFileMetaArg(&DestList);
return (SHELL_SUCCESS);
}
/**
Function to clean up paths.
- Single periods in the path are removed.
- Double periods in the path are removed along with a single parent directory.
- Forward slashes L'/' are converted to backward slashes L'\'.
This will be done inline and the existing buffer may be larger than required
upon completion.
@param[in] Path The pointer to the string containing the path.
@retval NULL An error occured.
@return Path in all other instances.
**/
CHAR16*
EFIAPI
PathCleanUpDirectories(
IN CHAR16 *Path
)
{
CHAR16 *TempString;
UINTN TempSize;
if (Path==NULL) {
return(NULL);
}
//
// Fix up the '/' vs '\'
//
for (TempString = Path ; TempString != NULL && *TempString != CHAR_NULL ; TempString++) {
if (*TempString == L'/') {
*TempString = L'\\';
}
}
//
// Fix up the ..
//
while ((TempString = StrStr(Path, L"\\..\\")) != NULL) {
*TempString = CHAR_NULL;
TempString += 4;
PathRemoveLastItem(Path);
TempSize = StrSize(TempString);
CopyMem(Path+StrLen(Path), TempString, TempSize);
}
if ((TempString = StrStr(Path, L"\\..")) != NULL && *(TempString + 3) == CHAR_NULL) {
*TempString = CHAR_NULL;
PathRemoveLastItem(Path);
}
//
// Fix up the .
//
while ((TempString = StrStr(Path, L"\\.\\")) != NULL) {
*TempString = CHAR_NULL;
TempString += 2;
TempSize = StrSize(TempString);
CopyMem(Path+StrLen(Path), TempString, TempSize);
}
if ((TempString = StrStr(Path, L"\\.")) != NULL && *(TempString + 2) == CHAR_NULL) {
*(TempString + 1) = CHAR_NULL;
}
return (Path);
}
STATIC EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollation = NULL;
/**
Function to compare 2 strings without regard to case of the characters.
@param[in] Buffer1 Pointer to String to compare.
@param[in] Buffer2 Pointer to second String to compare.
@retval 0 Buffer1 equal to Buffer2.
@return < 0 Buffer1 is less than Buffer2.
@return > 0 Buffer1 is greater than Buffer2.
**/
INTN
EFIAPI
StringNoCaseCompare (
IN CONST VOID *Buffer1,
IN CONST VOID *Buffer2
)
{
EFI_STATUS Status;
if (mUnicodeCollation == NULL) {
Status = gBS->LocateProtocol(
&gEfiUnicodeCollation2ProtocolGuid,
NULL,
(VOID**)&mUnicodeCollation);
ASSERT(Status);
}
return (mUnicodeCollation->StriColl(
mUnicodeCollation,
*(CHAR16**)Buffer1,
*(CHAR16**)Buffer2));
}
/**
Function to validate that moving a specific file (FileName) to a specific
location (DestPath) is valid.
This function will verify that the destination is not a subdirectory of
FullName, that the Current working Directory is not being moved, and that
the directory is not read only.
if the move is invalid this function will report the error to StdOut.
@param FullName [in] The name of the file to move.
@param Cwd [in] The current working directory
@param DestPath [in] The target location to move to
@param Attribute[in] The Attribute of the file
@retval TRUE The move is valid
@retval FALSE The move is not
**/
BOOLEAN
EFIAPI
IsValidMove(
IN CONST CHAR16 *FullName,
IN CONST CHAR16 *Cwd,
IN CONST CHAR16 *DestPath,
IN CONST UINT64 Attribute
)
{
CHAR16 *Test;
CHAR16 *Test1;
CHAR16 *TestWalker;
INTN Result;
UINTN TempLen;
if (Cwd != NULL && StrCmp(FullName, Cwd) == 0) {
//
// Invalid move
//
Print(STR_MV_INV_CWD);
return (FALSE);
}
Test = NULL;
Test = StrnCatGrow(&Test, NULL, DestPath, 0);
TestWalker = Test;
ASSERT(TestWalker != NULL);
while(*TestWalker == L'\\') {
TestWalker++;
}
while(TestWalker != NULL && TestWalker[StrLen(TestWalker)-1] == L'\\') {
TestWalker[StrLen(TestWalker)-1] = CHAR_NULL;
}
ASSERT(TestWalker != NULL);
ASSERT(FullName != NULL);
if (StrStr(FullName, TestWalker) != 0) {
TempLen = StrLen(FullName);
if (StrStr(FullName, TestWalker) != FullName // not the first items... (could below it)
&& TempLen <= (StrLen(TestWalker) + 1)
&& StrStr(FullName+StrLen(TestWalker) + 1, L"\\") == NULL) {
//
// Invalid move
//
Print(STR_MV_INV_SUB);
FreePool(Test);
return (FALSE);
}
}
FreePool(Test);
if (StrStr(DestPath, FullName) != 0 && StrStr(DestPath, FullName) != DestPath) {
//
// Invalid move
//
Print(STR_MV_INV_SUB);
return (FALSE);
}
if ((Attribute & EFI_FILE_READ_ONLY) != 0) {
//
// invalid to move read only
//
Print(STR_MV_INV_RO);
return (FALSE);
}
Test = StrStr(FullName, L":");
Test1 = StrStr(DestPath, L":");
if (Test1 != NULL && Test != NULL) {
*Test = CHAR_NULL;
*Test1 = CHAR_NULL;
Result = StringNoCaseCompare(&FullName, &DestPath);
*Test = L':';
*Test1 = L':';
if (Result != 0) {
Print(STR_MV_INV_FS);
return (FALSE);
}
}
return (TRUE);
}
/**
function to take a list of files to move and a destination location and do
the verification and moving of those files to that location. This function
will report any errors to the user and continue to move the rest of the files.
@param[in] FileList A LIST_ENTRY* based list of files to move
@param[out] Resp pointer to response from question. Pass back on looped calling
@param[in] DestDir the destination location
@retval SHELL_SUCCESS the files were all moved.
@retval SHELL_INVALID_PARAMETER a parameter was invalid
@retval SHELL_SECURITY_VIOLATION a security violation ocurred
@retval SHELL_WRITE_PROTECTED the destination was write protected
@retval SHELL_OUT_OF_RESOURCES a memory allocation failed
**/
SHELL_STATUS
EFIAPI
ValidateAndMoveFiles(
IN CONST EFI_SHELL_FILE_INFO *FileList,
OUT VOID **Resp,
IN CONST CHAR16 *DestDir
)
{
EFI_STATUS Status;
CHAR16 *DestPath;
CONST CHAR16 *Cwd;
SHELL_STATUS ShellStatus;
CONST EFI_SHELL_FILE_INFO *Node;
EFI_FILE_INFO *NewFileInfo;
CHAR16 *TempLocation;
UINTN NewSize;
UINTN Length;
VOID *Response;
SHELL_FILE_HANDLE DestHandle;
CHAR16 STR_GEN_DEST_EXIST_OVR[] = L"Destination file already exists. Overwrite? Yes, No, All, Cancel ";
ASSERT(FileList != NULL);
ASSERT(DestDir != NULL);
DestPath = NULL;
Cwd = ShellGetCurrentDir(NULL);
Response = *Resp;
//
// Get and validate the destination location
//
ShellStatus = GetDestinationLocation(DestDir, &DestPath, Cwd);
if (ShellStatus != SHELL_SUCCESS) {
return (ShellStatus);
}
DestPath = PathCleanUpDirectories(DestPath);
//
// Go through the list of files and directories to move...
//
for (Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&FileList->Link)
; !IsNull(&FileList->Link, &Node->Link)
; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&FileList->Link, &Node->Link)
){
if (ShellGetExecutionBreakFlag()) {
break;
}
ASSERT(Node->FileName != NULL);
ASSERT(Node->FullName != NULL);
//
// skip the directory traversing stuff...
//
if (StrCmp(Node->FileName, L".") == 0 || StrCmp(Node->FileName, L"..") == 0) {
continue;
}
//
// Validate that the move is valid
//
if (!IsValidMove(Node->FullName, Cwd, DestPath, Node->Info->Attribute)) {
ShellStatus = SHELL_INVALID_PARAMETER;
continue;
}
//
// Chop off map info from "DestPath"
//
if ((TempLocation = StrStr(DestPath, L":")) != NULL) {
CopyMem(DestPath, TempLocation+1, StrSize(TempLocation+1));
}
//
// construct the new file info block
//
NewSize = StrSize(DestPath);
NewSize += StrSize(Node->FileName) + SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16);
NewFileInfo = AllocateZeroPool(NewSize);
if (NewFileInfo == NULL) {
Print(STR_GEN_NO_MEM);
ShellStatus = SHELL_OUT_OF_RESOURCES;
} else {
CopyMem(NewFileInfo, Node->Info, SIZE_OF_EFI_FILE_INFO);
if (DestPath[0] != L'\\') {
StrCpy(NewFileInfo->FileName, L"\\");
StrCat(NewFileInfo->FileName, DestPath);
} else {
StrCpy(NewFileInfo->FileName, DestPath);
}
Length = StrLen(NewFileInfo->FileName);
if (Length > 0) {
Length--;
}
if (NewFileInfo->FileName[Length] == L'\\') {
if (Node->FileName[0] == L'\\') {
//
// Don't allow for double slashes. Eliminate one of them.
//
NewFileInfo->FileName[Length] = CHAR_NULL;
}
StrCat(NewFileInfo->FileName, Node->FileName);
}
NewFileInfo->Size = SIZE_OF_EFI_FILE_INFO + StrSize(NewFileInfo->FileName);
Print(STR_MV_OUTPUT, Node->FullName, NewFileInfo->FileName);
if (!EFI_ERROR(ShellFileExists(NewFileInfo->FileName))) {
if (Response == NULL) {
ShellPromptForResponse(ShellPromptResponseTypeYesNoAllCancel, STR_GEN_DEST_EXIST_OVR, &Response);
}
switch (*(SHELL_PROMPT_RESPONSE*)Response) {
case ShellPromptResponseNo:
FreePool(NewFileInfo);
continue;
case ShellPromptResponseCancel:
*Resp = Response;
//
// indicate to stop everything
//
FreePool(NewFileInfo);
FreePool(DestPath);
return (SHELL_ABORTED);
case ShellPromptResponseAll:
*Resp = Response;
break;
case ShellPromptResponseYes:
FreePool(Response);
break;
default:
FreePool(Response);
FreePool(NewFileInfo);
FreePool(DestPath);
return SHELL_ABORTED;
}
Status = ShellOpenFileByName(NewFileInfo->FileName, &DestHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0);
ShellDeleteFile(&DestHandle);
}
//
// Perform the move operation
//
Status = ShellSetFileInfo(Node->Handle, NewFileInfo);
//
// Free the info object we used...
//
FreePool(NewFileInfo);
//
// Check our result
//
if (EFI_ERROR(Status)) {
Print(STR_GEN_ERR_UK, Status);
ShellStatus = SHELL_INVALID_PARAMETER;
if (Status == EFI_SECURITY_VIOLATION) {
ShellStatus = SHELL_SECURITY_VIOLATION;
} else if (Status == EFI_WRITE_PROTECTED) {
ShellStatus = SHELL_WRITE_PROTECTED;
} else if (Status == EFI_OUT_OF_RESOURCES) {
ShellStatus = SHELL_OUT_OF_RESOURCES;
} else if (Status == EFI_DEVICE_ERROR) {
ShellStatus = SHELL_DEVICE_ERROR;
} else if (Status == EFI_ACCESS_DENIED) {
ShellStatus = SHELL_ACCESS_DENIED;
}
} else {
Print( L"%s", STR_GEN_RES_OK);
}
}
} // for loop
FreePool(DestPath);
return (ShellStatus);
}
int
EFIAPI
main (
IN int Argc,
IN CHAR16 **Argv
)
{
EFI_STATUS Status;
LIST_ENTRY *Package;
CHAR16 *ProblemParam;
SHELL_STATUS ShellStatus;
UINTN ParamCount;
UINTN LoopCounter;
EFI_SHELL_FILE_INFO *FileList;
VOID *Response;
ProblemParam = NULL;
ShellStatus = SHELL_SUCCESS;
ParamCount = 0;
FileList = NULL;
Response = NULL;
//
// parse the command line
//
Status = ShellCommandLineParse (EmptyParamList, &Package, &ProblemParam, TRUE);
if (EFI_ERROR(Status)) {
if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
Print(STR_GEN_PROBLEM , ProblemParam);
FreePool(ProblemParam);
ShellStatus = SHELL_INVALID_PARAMETER;
} else {
ASSERT(FALSE);
}
} else {
//
// check for "-?"
//
if (ShellCommandLineGetFlag(Package, L"-?")) {
ASSERT(FALSE);
}
switch (ParamCount = ShellCommandLineGetCount(Package)) {
case 0:
case 1:
//
// we have insufficient parameters
//
Print(STR_GEN_TOO_FEW);
ShellStatus = SHELL_INVALID_PARAMETER;
break;
case 2:
//
// must have valid CWD for single parameter...
//
if (ShellGetCurrentDir(NULL) == NULL){
Print(STR_GEN_NO_CWD);
ShellStatus = SHELL_INVALID_PARAMETER;
} else {
Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, 1), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
Print(STR_GEN_FILE_NF, ShellCommandLineGetRawValue(Package, 1));
ShellStatus = SHELL_NOT_FOUND;
} else {
//
// ValidateAndMoveFiles will report errors to the screen itself
//
ShellStatus = ValidateAndMoveFiles(FileList, &Response, ShellGetCurrentDir(NULL));
}
}
break;
default:
///@todo make sure this works with error half way through and continues...
for (ParamCount--, LoopCounter = 1 ; LoopCounter < ParamCount ; LoopCounter++) {
if (ShellGetExecutionBreakFlag()) {
break;
}
Status = ShellOpenFileMetaArg((CHAR16*)ShellCommandLineGetRawValue(Package, LoopCounter), EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
if (FileList == NULL || IsListEmpty(&FileList->Link) || EFI_ERROR(Status)) {
Print(STR_GEN_FILE_NF, ShellCommandLineGetRawValue(Package, LoopCounter));
ShellStatus = SHELL_NOT_FOUND;
} else {
//
// ValidateAndMoveFiles will report errors to the screen itself
// Only change ShellStatus if it's sucessful
//
if (ShellStatus == SHELL_SUCCESS) {
ShellStatus = ValidateAndMoveFiles(FileList, &Response, ShellCommandLineGetRawValue(Package, ParamCount));
} else {
ValidateAndMoveFiles(FileList, &Response, ShellCommandLineGetRawValue(Package, ParamCount));
}
}
if (FileList != NULL && !IsListEmpty(&FileList->Link)) {
Status = ShellCloseFileMetaArg(&FileList);
if (EFI_ERROR(Status) && ShellStatus == SHELL_SUCCESS) {
ShellStatus = SHELL_ACCESS_DENIED;
Print(STR_GEN_ERR_FILE , ShellCommandLineGetRawValue(Package, 1), ShellStatus|MAX_BIT);
}
}
}
break;
} // switch on parameter count
if (FileList != NULL) {
ShellCloseFileMetaArg(&FileList);
}
//
// free the command line package
//
ShellCommandLineFreeVarList (Package);
}
SHELL_FREE_NON_NULL(Response);
if (ShellGetExecutionBreakFlag()) {
return (SHELL_ABORTED);
}
return EFI_SUCCESS;
}
运行结果:
完整代码下载:
zMv