]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellLevel2CommandsLib/Parse.c
ShellPkg: Handle escape characters properly for parse command
[mirror_edk2.git] / ShellPkg / Library / UefiShellLevel2CommandsLib / Parse.c
1 /** @file
2 Main file for Parse shell level 2 function.
3
4 (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
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 "UefiShellLevel2CommandsLib.h"
17
18 /**
19 Check if data is coming from StdIn output.
20
21 @param[in] None
22
23 @retval TRUE StdIn stream data available to parse
24 @retval FALSE StdIn stream data is not available to parse.
25 **/
26 BOOLEAN
27 IsStdInDataAvailable (
28 VOID
29 )
30 {
31 SHELL_FILE_HANDLE FileHandle;
32 EFI_STATUS Status;
33 CHAR16 CharBuffer;
34 UINTN CharSize;
35 UINT64 OriginalFilePosition;
36
37 Status = EFI_SUCCESS;
38 FileHandle = NULL;
39 OriginalFilePosition = 0;
40
41 if (ShellOpenFileByName (L">i", &FileHandle, EFI_FILE_MODE_READ, 0) == EFI_SUCCESS) {
42 CharSize = sizeof(CHAR16);
43 gEfiShellProtocol->GetFilePosition (FileHandle, &OriginalFilePosition);
44 Status = gEfiShellProtocol->ReadFile (FileHandle, &CharSize, &CharBuffer);
45 if (EFI_ERROR (Status) || (CharSize != sizeof(CHAR16))) {
46 return FALSE;
47 }
48 gEfiShellProtocol->SetFilePosition(FileHandle, OriginalFilePosition);
49 }
50
51 if (FileHandle == NULL) {
52 return FALSE;
53 } else {
54 return TRUE;
55 }
56 }
57
58 /**
59 Function to read a single line (up to but not including the \n) using StdIn data from a SHELL_FILE_HANDLE.
60
61 If the position upon start is 0, then the Ascii Boolean will be set. This should be
62 maintained and not changed for all operations with the same file.
63
64 @param[in] Handle SHELL_FILE_HANDLE to read from.
65 @param[in, out] Buffer The pointer to buffer to read into.
66 @param[in, out] Size The pointer to number of bytes in Buffer.
67 @param[in] Truncate If the buffer is large enough, this has no effect.
68 If the buffer is is too small and Truncate is TRUE,
69 the line will be truncated.
70 If the buffer is is too small and Truncate is FALSE,
71 then no read will occur.
72
73 @retval EFI_SUCCESS The operation was successful. The line is stored in
74 Buffer.
75 @retval EFI_INVALID_PARAMETER Handle was NULL.
76 @retval EFI_INVALID_PARAMETER Size was NULL.
77 @retval EFI_BUFFER_TOO_SMALL Size was not large enough to store the line.
78 Size was updated to the minimum space required.
79 **/
80 EFI_STATUS
81 EFIAPI
82 ShellFileHandleReadStdInLine(
83 IN SHELL_FILE_HANDLE Handle,
84 IN OUT CHAR16 *Buffer,
85 IN OUT UINTN *Size,
86 IN BOOLEAN Truncate
87 )
88 {
89 EFI_STATUS Status;
90 CHAR16 CharBuffer;
91 UINTN CharSize;
92 UINTN CountSoFar;
93 UINT64 OriginalFilePosition;
94
95
96 if (Handle == NULL
97 ||Size == NULL
98 ){
99 return (EFI_INVALID_PARAMETER);
100 }
101 if (Buffer == NULL) {
102 ASSERT(*Size == 0);
103 } else {
104 *Buffer = CHAR_NULL;
105 }
106 gEfiShellProtocol->GetFilePosition (Handle, &OriginalFilePosition);
107
108 for (CountSoFar = 0;;CountSoFar++){
109 CharBuffer = 0;
110 CharSize = sizeof(CHAR16);
111 Status = gEfiShellProtocol->ReadFile (Handle, &CharSize, &CharBuffer);
112 if ( EFI_ERROR(Status)
113 || CharSize == 0
114 || (CharBuffer == L'\n')
115 ){
116 break;
117 }
118 //
119 // if we have space save it...
120 //
121 if ((CountSoFar+1)*sizeof(CHAR16) < *Size){
122 ASSERT(Buffer != NULL);
123 ((CHAR16*)Buffer)[CountSoFar] = CharBuffer;
124 ((CHAR16*)Buffer)[CountSoFar+1] = CHAR_NULL;
125 }
126 }
127
128 //
129 // if we ran out of space tell when...
130 //
131 if ((CountSoFar+1)*sizeof(CHAR16) > *Size){
132 *Size = (CountSoFar+1)*sizeof(CHAR16);
133 if (!Truncate) {
134 gEfiShellProtocol->SetFilePosition(Handle, OriginalFilePosition);
135 } else {
136 DEBUG((DEBUG_WARN, "The line was truncated in ShellFileHandleReadLine"));
137 }
138 return (EFI_BUFFER_TOO_SMALL);
139 }
140 while(Buffer[StrLen(Buffer)-1] == L'\r') {
141 Buffer[StrLen(Buffer)-1] = CHAR_NULL;
142 }
143
144 return (Status);
145 }
146
147
148 /**
149 Function to read a single line using StdIn from a SHELL_FILE_HANDLE. The \n is not included in the returned
150 buffer. The returned buffer must be callee freed.
151
152 If the position upon start is 0, then the Ascii Boolean will be set. This should be
153 maintained and not changed for all operations with the same file.
154
155 @param[in] Handle SHELL_FILE_HANDLE to read from.
156
157 @return The line of text from the file.
158 @retval NULL There was not enough memory available.
159
160 @sa ShellFileHandleReadLine
161 **/
162 CHAR16*
163 EFIAPI
164 ParseReturnStdInLine (
165 IN SHELL_FILE_HANDLE Handle
166 )
167 {
168 CHAR16 *RetVal;
169 UINTN Size;
170 EFI_STATUS Status;
171
172 Size = 0;
173 RetVal = NULL;
174
175 Status = ShellFileHandleReadStdInLine (Handle, RetVal, &Size, FALSE);
176 if (Status == EFI_BUFFER_TOO_SMALL) {
177 RetVal = AllocateZeroPool(Size);
178 if (RetVal == NULL) {
179 return (NULL);
180 }
181 Status = ShellFileHandleReadStdInLine (Handle, RetVal, &Size, FALSE);
182
183 }
184 if (EFI_ERROR(Status) && (RetVal != NULL)) {
185 FreePool(RetVal);
186 RetVal = NULL;
187 }
188 return (RetVal);
189 }
190
191 /**
192 Handle stings for SFO Output with escape character ^ in a string
193 1. Quotation marks in the string must be escaped by using a ^ character (i.e. ^\94).
194 2. The ^ character may be inserted using ^^.
195
196 @param[in] String The Unicode NULL-terminated string.
197
198 @retval NewString The new string handled for SFO.
199 **/
200 EFI_STRING
201 HandleStringWithEscapeCharForParse (
202 IN CHAR16 *String
203 )
204 {
205 EFI_STRING NewStr;
206 EFI_STRING StrWalker;
207 EFI_STRING ReturnStr;
208
209 if (String == NULL) {
210 return NULL;
211 }
212
213 //
214 // start to parse the input string.
215 //
216 NewStr = AllocateZeroPool (StrSize (String));
217 if (NewStr == NULL) {
218 return NULL;
219 }
220 ReturnStr = NewStr;
221 StrWalker = String;
222 while (*StrWalker != CHAR_NULL) {
223 if (*StrWalker == L'^' && (*(StrWalker + 1) == L'^' || *(StrWalker + 1) == L'"')) {
224 *NewStr = *(StrWalker + 1);
225 StrWalker++;
226 } else {
227 *NewStr = *StrWalker;
228 }
229 StrWalker++;
230 NewStr++;
231 }
232
233 return ReturnStr;
234 }
235
236
237 /**
238 Do the actual parsing of the file. the file should be SFO output from a
239 shell command or a similar format.
240
241 @param[in] FileName The filename to open.
242 @param[in] TableName The name of the table to find.
243 @param[in] ColumnIndex The column number to get.
244 @param[in] TableNameInstance Which instance of the table to get (row).
245 @param[in] ShellCommandInstance Which instance of the command to get.
246 @param[in] StreamingUnicode Indicates Input file is StdIn Unicode streaming data or not
247
248 @retval SHELL_NOT_FOUND The requested instance was not found.
249 @retval SHELL_SUCCESS The operation was successful.
250 **/
251 SHELL_STATUS
252 EFIAPI
253 PerformParsing(
254 IN CONST CHAR16 *FileName,
255 IN CONST CHAR16 *TableName,
256 IN CONST UINTN ColumnIndex,
257 IN CONST UINTN TableNameInstance,
258 IN CONST UINTN ShellCommandInstance,
259 IN BOOLEAN StreamingUnicode
260 )
261 {
262 SHELL_FILE_HANDLE FileHandle;
263 EFI_STATUS Status;
264 BOOLEAN Ascii;
265 UINTN LoopVariable;
266 UINTN ColumnLoop;
267 CHAR16 *TempLine;
268 CHAR16 *ColumnPointer;
269 SHELL_STATUS ShellStatus;
270 CHAR16 *TempSpot;
271 CHAR16 *SfoString;
272
273 ASSERT(FileName != NULL);
274 ASSERT(TableName != NULL);
275
276 ShellStatus = SHELL_SUCCESS;
277
278 Status = ShellOpenFileByName(FileName, &FileHandle, EFI_FILE_MODE_READ, 0);
279 if (EFI_ERROR(Status)) {
280 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL), gShellLevel2HiiHandle, L"parse", FileName);
281 ShellStatus = SHELL_NOT_FOUND;
282 } else if (!EFI_ERROR (FileHandleIsDirectory (FileHandle))) {
283 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_NOT_FILE), gShellLevel2HiiHandle, L"parse", FileName);
284 ShellStatus = SHELL_NOT_FOUND;
285 } else {
286 for (LoopVariable = 0 ; LoopVariable < ShellCommandInstance && !ShellFileHandleEof(FileHandle);) {
287 if (StreamingUnicode) {
288 TempLine = ParseReturnStdInLine (FileHandle);
289 } else {
290 TempLine = ShellFileHandleReturnLine (FileHandle, &Ascii);
291 }
292
293 if ((TempLine == NULL) || (*TempLine == CHAR_NULL && StreamingUnicode == TRUE)) {
294 break;
295 }
296
297 //
298 // Search for "ShellCommand," in the file to start the SFO table
299 // for a given ShellCommand. The UEFI Shell spec does not specify
300 // a space after the comma.
301 //
302 if (StrStr (TempLine, L"ShellCommand,") == TempLine) {
303 LoopVariable++;
304 }
305 SHELL_FREE_NON_NULL(TempLine);
306 }
307 if (LoopVariable == ShellCommandInstance) {
308 LoopVariable = 0;
309 while(1) {
310 if (StreamingUnicode) {
311 TempLine = ParseReturnStdInLine (FileHandle);
312 } else {
313 TempLine = ShellFileHandleReturnLine (FileHandle, &Ascii);
314 }
315 if (TempLine == NULL
316 || *TempLine == CHAR_NULL
317 || StrStr (TempLine, L"ShellCommand,") == TempLine) {
318 SHELL_FREE_NON_NULL(TempLine);
319 break;
320 }
321 if (StrStr (TempLine, TableName) == TempLine) {
322 LoopVariable++;
323 if (LoopVariable == TableNameInstance
324 || (TableNameInstance == (UINTN)-1)) {
325 for (ColumnLoop = 1, ColumnPointer = TempLine; ColumnLoop < ColumnIndex && ColumnPointer != NULL && *ColumnPointer != CHAR_NULL; ColumnLoop++) {
326 ColumnPointer = StrStr (ColumnPointer, L",\"");
327 if (ColumnPointer != NULL && *ColumnPointer != CHAR_NULL){
328 ColumnPointer++;
329 }
330 }
331 if (ColumnLoop == ColumnIndex) {
332 if (ColumnPointer == NULL) {
333 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_VALUE), gShellLevel2HiiHandle, L"parse", L"Column Index");
334 ShellStatus = SHELL_INVALID_PARAMETER;
335 } else {
336 TempSpot = StrStr (ColumnPointer, L",\"");
337 if (TempSpot != NULL) {
338 *TempSpot = CHAR_NULL;
339 }
340 while (ColumnPointer != NULL && *ColumnPointer != CHAR_NULL && ColumnPointer[0] == L' '){
341 ColumnPointer++;
342 }
343 if (ColumnPointer != NULL && *ColumnPointer != CHAR_NULL && ColumnPointer[0] == L'\"'){
344 ColumnPointer++;
345 }
346 if (ColumnPointer != NULL && *ColumnPointer != CHAR_NULL && ColumnPointer[StrLen (ColumnPointer) - 1] == L'\"'){
347 ColumnPointer[StrLen (ColumnPointer) - 1] = CHAR_NULL;
348 }
349 SfoString = HandleStringWithEscapeCharForParse (ColumnPointer);
350 if (SfoString != NULL) {
351 ShellPrintEx (-1, -1, L"%s\r\n", SfoString);
352 SHELL_FREE_NON_NULL (SfoString);
353 }
354 }
355 }
356 }
357 }
358 SHELL_FREE_NON_NULL(TempLine);
359 }
360 }
361 }
362 return (ShellStatus);
363 }
364
365 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
366 {L"-i", TypeValue},
367 {L"-s", TypeValue},
368 {NULL, TypeMax}
369 };
370
371 /**
372 Function for 'parse' command.
373
374 @param[in] ImageHandle Handle to the Image (NULL if Internal).
375 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
376 **/
377 SHELL_STATUS
378 EFIAPI
379 ShellCommandRunParse (
380 IN EFI_HANDLE ImageHandle,
381 IN EFI_SYSTEM_TABLE *SystemTable
382 )
383 {
384 EFI_STATUS Status;
385 LIST_ENTRY *Package;
386 CHAR16 *ProblemParam;
387 CONST CHAR16 *FileName;
388 CONST CHAR16 *TableName;
389 CONST CHAR16 *ColumnString;
390 SHELL_STATUS ShellStatus;
391 UINTN ShellCommandInstance;
392 UINTN TableNameInstance;
393 BOOLEAN StreamingUnicode;
394
395 ShellStatus = SHELL_SUCCESS;
396 ProblemParam = NULL;
397 StreamingUnicode = FALSE;
398
399 //
400 // initialize the shell lib (we must be in non-auto-init...)
401 //
402 Status = ShellInitialize();
403 ASSERT_EFI_ERROR(Status);
404
405 //
406 // parse the command line
407 //
408 Status = ShellCommandLineParseEx (ParamList, &Package, &ProblemParam, TRUE, FALSE);
409 if (EFI_ERROR(Status)) {
410 if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
411 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"parse", ProblemParam);
412 FreePool(ProblemParam);
413 ShellStatus = SHELL_INVALID_PARAMETER;
414 } else {
415 ASSERT(FALSE);
416 }
417 } else {
418 StreamingUnicode = IsStdInDataAvailable ();
419 if ((!StreamingUnicode && (ShellCommandLineGetCount(Package) < 4)) ||
420 (ShellCommandLineGetCount(Package) < 3)) {
421 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel2HiiHandle, L"parse");
422 ShellStatus = SHELL_INVALID_PARAMETER;
423 } else if ((StreamingUnicode && (ShellCommandLineGetCount(Package) > 3)) ||
424 (ShellCommandLineGetCount(Package) > 4)) {
425 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle, L"parse");
426 ShellStatus = SHELL_INVALID_PARAMETER;
427 } else {
428 if (StreamingUnicode) {
429 FileName = L">i";
430 TableName = ShellCommandLineGetRawValue(Package, 1);
431 ColumnString = ShellCommandLineGetRawValue(Package, 2);
432 } else {
433 FileName = ShellCommandLineGetRawValue(Package, 1);
434 TableName = ShellCommandLineGetRawValue(Package, 2);
435 ColumnString = ShellCommandLineGetRawValue(Package, 3);
436 }
437 if (ShellCommandLineGetValue(Package, L"-i") == NULL) {
438 TableNameInstance = (UINTN)-1;
439 } else {
440 TableNameInstance = ShellStrToUintn(ShellCommandLineGetValue(Package, L"-i"));
441 }
442 if (ShellCommandLineGetValue(Package, L"-s") == NULL) {
443 ShellCommandInstance = 1;
444 } else {
445 ShellCommandInstance = ShellStrToUintn(ShellCommandLineGetValue(Package, L"-s"));
446 }
447
448 ShellStatus = PerformParsing(FileName, TableName, ShellStrToUintn(ColumnString), TableNameInstance, ShellCommandInstance, StreamingUnicode);
449 }
450 }
451
452 //
453 // free the command line package
454 //
455 ShellCommandLineFreeVarList (Package);
456
457 return (ShellStatus);
458 }
459