1 /** @file
2 This contains some useful functions for parsing INF files.
3 
4 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include <assert.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include <stdlib.h>
13 #include "EfiUtilityMsgs.h"
14 #include "ParseInf.h"
15 #include "CommonLib.h"
16 
17 CHAR8 *
ReadLine(IN MEMORY_FILE * InputFile,IN OUT CHAR8 * InputBuffer,IN UINTN MaxLength)18 ReadLine (
19   IN MEMORY_FILE    *InputFile,
20   IN OUT CHAR8      *InputBuffer,
21   IN UINTN          MaxLength
22   )
23 /*++
24 
25 Routine Description:
26 
27   This function reads a line, stripping any comments.
28   The function reads a string from the input stream argument and stores it in
29   the input string. ReadLine reads characters from the current file position
30   to and including the first newline character, to the end of the stream, or
31   until the number of characters read is equal to MaxLength - 1, whichever
32   comes first.  The newline character, if read, is replaced with a \0.
33 
34 Arguments:
35 
36   InputFile     Memory file image.
37   InputBuffer   Buffer to read into, must be MaxLength size.
38   MaxLength     The maximum size of the input buffer.
39 
40 Returns:
41 
42   NULL if error or EOF
43   InputBuffer otherwise
44 
45 --*/
46 {
47   CHAR8 *CharPtr;
48   CHAR8 *EndOfLine;
49   UINTN CharsToCopy;
50 
51   //
52   // Verify input parameters are not null
53   //
54   assert (InputBuffer);
55   assert (InputFile->FileImage);
56   assert (InputFile->Eof);
57   assert (InputFile->CurrentFilePointer);
58 
59   //
60   // Check for end of file condition
61   //
62   if (InputFile->CurrentFilePointer >= InputFile->Eof) {
63     return NULL;
64   }
65   //
66   // Find the next newline char
67   //
68   EndOfLine = strchr (InputFile->CurrentFilePointer, '\n');
69 
70   //
71   // Determine the number of characters to copy.
72   //
73   if (EndOfLine == 0) {
74     //
75     // If no newline found, copy to the end of the file.
76     //
77     CharsToCopy = InputFile->Eof - InputFile->CurrentFilePointer;
78   } else if (EndOfLine >= InputFile->Eof) {
79     //
80     // If the newline found was beyond the end of file, copy to the eof.
81     //
82     CharsToCopy = InputFile->Eof - InputFile->CurrentFilePointer;
83   } else {
84     //
85     // Newline found in the file.
86     //
87     CharsToCopy = EndOfLine - InputFile->CurrentFilePointer;
88   }
89   //
90   // If the end of line is too big for the current buffer, set it to the max
91   // size of the buffer (leaving room for the \0.
92   //
93   if (CharsToCopy > MaxLength - 1) {
94     CharsToCopy = MaxLength - 1;
95   }
96   //
97   // Copy the line.
98   //
99   memcpy (InputBuffer, InputFile->CurrentFilePointer, CharsToCopy);
100 
101   //
102   // Add the null termination over the 0x0D
103   //
104   if (InputBuffer[CharsToCopy - 1] == '\r') {
105 
106     InputBuffer[CharsToCopy - 1] = '\0';
107 
108   } else {
109 
110     InputBuffer[CharsToCopy] = '\0';
111 
112   }
113 
114   //
115   // Increment the current file pointer (include the 0x0A)
116   //
117   InputFile->CurrentFilePointer += CharsToCopy + 1;
118 
119   //
120   // Strip any comments
121   //
122   CharPtr = strstr (InputBuffer, "//");
123   if (CharPtr != 0) {
124     CharPtr[0] = 0;
125   }
126   //
127   // Return the string
128   //
129   return InputBuffer;
130 }
131 
132 BOOLEAN
FindSection(IN MEMORY_FILE * InputFile,IN CHAR8 * Section)133 FindSection (
134   IN MEMORY_FILE    *InputFile,
135   IN CHAR8          *Section
136   )
137 /*++
138 
139 Routine Description:
140 
141   This function parses a file from the beginning to find a section.
142   The section string may be anywhere within a line.
143 
144 Arguments:
145 
146   InputFile     Memory file image.
147   Section       Section to search for
148 
149 Returns:
150 
151   FALSE if error or EOF
152   TRUE if section found
153 
154 --*/
155 {
156   CHAR8 InputBuffer[MAX_LONG_FILE_PATH];
157   CHAR8 *CurrentToken;
158 
159   //
160   // Verify input is not NULL
161   //
162   assert (InputFile->FileImage);
163   assert (InputFile->Eof);
164   assert (InputFile->CurrentFilePointer);
165   assert (Section);
166 
167   //
168   // Rewind to beginning of file
169   //
170   InputFile->CurrentFilePointer = InputFile->FileImage;
171 
172   //
173   // Read lines until the section is found
174   //
175   while (InputFile->CurrentFilePointer < InputFile->Eof) {
176     //
177     // Read a line
178     //
179     ReadLine (InputFile, InputBuffer, MAX_LONG_FILE_PATH);
180 
181     //
182     // Check if the section is found
183     //
184     CurrentToken = strstr (InputBuffer, Section);
185     if (CurrentToken != NULL) {
186       return TRUE;
187     }
188   }
189 
190   return FALSE;
191 }
192 
193 EFI_STATUS
FindToken(IN MEMORY_FILE * InputFile,IN CHAR8 * Section,IN CHAR8 * Token,IN UINTN Instance,OUT CHAR8 * Value)194 FindToken (
195   IN MEMORY_FILE    *InputFile,
196   IN CHAR8          *Section,
197   IN CHAR8          *Token,
198   IN UINTN          Instance,
199   OUT CHAR8         *Value
200   )
201 /*++
202 
203 Routine Description:
204 
205   Finds a token value given the section and token to search for.
206 
207 Arguments:
208 
209   InputFile Memory file image.
210   Section   The section to search for, a string within [].
211   Token     The token to search for, e.g. EFI_PEIM_RECOVERY, followed by an = in the INF file.
212   Instance  The instance of the token to search for.  Zero is the first instance.
213   Value     The string that holds the value following the =.  Must be MAX_LONG_FILE_PATH in size.
214 
215 Returns:
216 
217   EFI_SUCCESS             Value found.
218   EFI_ABORTED             Format error detected in INF file.
219   EFI_INVALID_PARAMETER   Input argument was null.
220   EFI_LOAD_ERROR          Error reading from the file.
221   EFI_NOT_FOUND           Section/Token/Value not found.
222 
223 --*/
224 {
225   CHAR8   InputBuffer[MAX_LONG_FILE_PATH];
226   CHAR8   *CurrentToken;
227   CHAR8   *Delimiter;
228   BOOLEAN ParseError;
229   BOOLEAN ReadError;
230   UINTN   Occurrence;
231 
232   //
233   // Check input parameters
234   //
235   if (InputFile->FileImage == NULL ||
236       InputFile->Eof == NULL ||
237       InputFile->CurrentFilePointer == NULL ||
238       Section == NULL ||
239       strlen (Section) == 0 ||
240       Token == NULL ||
241       strlen (Token) == 0 ||
242       Value == NULL
243       ) {
244     return EFI_INVALID_PARAMETER;
245   }
246   //
247   // Initialize error codes
248   //
249   ParseError  = FALSE;
250   ReadError   = FALSE;
251 
252   //
253   // Initialize our instance counter for the search token
254   //
255   Occurrence = 0;
256 
257   if (FindSection (InputFile, Section)) {
258     //
259     // Found the desired section, find and read the desired token
260     //
261     do {
262       //
263       // Read a line from the file
264       //
265       if (ReadLine (InputFile, InputBuffer, MAX_LONG_FILE_PATH) == NULL) {
266         //
267         // Error reading from input file
268         //
269         ReadError = TRUE;
270         break;
271       }
272       //
273       // Get the first non-whitespace string
274       //
275       Delimiter = strchr (InputBuffer, '=');
276       if (Delimiter != NULL) {
277         *Delimiter = 0;
278       }
279 
280       CurrentToken = strtok (InputBuffer, " \t\n");
281       if (CurrentToken == NULL || Delimiter == NULL) {
282         //
283         // Whitespace line found (or comment) so continue
284         //
285         CurrentToken = InputBuffer;
286         continue;
287       }
288       //
289       // Make sure we have not reached the end of the current section
290       //
291       if (CurrentToken[0] == '[') {
292         break;
293       }
294       //
295       // Compare the current token with the desired token
296       //
297       if (strcmp (CurrentToken, Token) == 0) {
298         //
299         // Found it
300         //
301         //
302         // Check if it is the correct instance
303         //
304         if (Instance == Occurrence) {
305           //
306           // Copy the contents following the =
307           //
308           CurrentToken = Delimiter + 1;
309           if (*CurrentToken == 0) {
310             //
311             // Nothing found, parsing error
312             //
313             ParseError = TRUE;
314           } else {
315             //
316             // Strip leading white space
317             //
318             while (*CurrentToken == ' ' || *CurrentToken == '\t') {
319               CurrentToken++;
320             }
321             //
322             // Copy the current token to the output value
323             //
324             strcpy (Value, CurrentToken);
325             //
326             // Strip trailing white space
327             //
328             while (strlen(Value) > 0 && (*(Value + strlen(Value) - 1) == ' ' || *(Value + strlen(Value) - 1) == '\t')) {
329               *(Value + strlen(Value) - 1) = 0;
330             }
331             return EFI_SUCCESS;
332           }
333         } else {
334           //
335           // Increment the occurrence found
336           //
337           Occurrence++;
338         }
339       }
340     } while (
341       !ParseError &&
342       !ReadError &&
343       InputFile->CurrentFilePointer < InputFile->Eof &&
344       CurrentToken[0] != '[' &&
345       Occurrence <= Instance
346     );
347   }
348   //
349   // Distinguish between read errors and INF file format errors.
350   //
351   if (ReadError) {
352     return EFI_LOAD_ERROR;
353   }
354 
355   if (ParseError) {
356     return EFI_ABORTED;
357   }
358 
359   return EFI_NOT_FOUND;
360 }
361 
362 EFI_STATUS
StringToGuid(IN CHAR8 * AsciiGuidBuffer,OUT EFI_GUID * GuidBuffer)363 StringToGuid (
364   IN CHAR8      *AsciiGuidBuffer,
365   OUT EFI_GUID  *GuidBuffer
366   )
367 /*++
368 
369 Routine Description:
370 
371   Converts a string to an EFI_GUID.  The string must be in the
372   xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format.
373 
374 Arguments:
375 
376   AsciiGuidBuffer - pointer to ascii string
377   GuidBuffer      - pointer to destination Guid
378 
379 Returns:
380 
381   EFI_ABORTED             Could not convert the string
382   EFI_SUCCESS             The string was successfully converted
383   EFI_INVALID_PARAMETER   Input parameter is invalid.
384 
385 --*/
386 {
387   INT32 Index;
388   int   Data1;
389   int   Data2;
390   int   Data3;
391   int   Data4[8];
392 
393   if (AsciiGuidBuffer == NULL || GuidBuffer == NULL) {
394     return EFI_INVALID_PARAMETER;
395   }
396   //
397   // Check Guid Format strictly xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
398   //
399   for (Index = 0; AsciiGuidBuffer[Index] != '\0' && Index < 37; Index ++) {
400     if (Index == 8 || Index == 13 || Index == 18 || Index == 23) {
401       if (AsciiGuidBuffer[Index] != '-') {
402         break;
403       }
404     } else {
405       if (((AsciiGuidBuffer[Index] >= '0') && (AsciiGuidBuffer[Index] <= '9')) ||
406          ((AsciiGuidBuffer[Index] >= 'a') && (AsciiGuidBuffer[Index] <= 'f')) ||
407          ((AsciiGuidBuffer[Index] >= 'A') && (AsciiGuidBuffer[Index] <= 'F'))) {
408         continue;
409       } else {
410         break;
411       }
412     }
413   }
414 
415   if (Index < 36 || AsciiGuidBuffer[36] != '\0') {
416     Error (NULL, 0, 1003, "Invalid option value", "Incorrect GUID \"%s\"\n  Correct Format \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"", AsciiGuidBuffer);
417     return EFI_ABORTED;
418   }
419 
420   //
421   // Scan the guid string into the buffer
422   //
423   Index = sscanf (
424             AsciiGuidBuffer,
425             "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
426             &Data1,
427             &Data2,
428             &Data3,
429             &Data4[0],
430             &Data4[1],
431             &Data4[2],
432             &Data4[3],
433             &Data4[4],
434             &Data4[5],
435             &Data4[6],
436             &Data4[7]
437             );
438 
439   //
440   // Verify the correct number of items were scanned.
441   //
442   if (Index != 11) {
443     Error (NULL, 0, 1003, "Invalid option value", "Incorrect GUID \"%s\"\n  Correct Format \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"", AsciiGuidBuffer);
444     return EFI_ABORTED;
445   }
446   //
447   // Copy the data into our GUID.
448   //
449   GuidBuffer->Data1     = (UINT32) Data1;
450   GuidBuffer->Data2     = (UINT16) Data2;
451   GuidBuffer->Data3     = (UINT16) Data3;
452   GuidBuffer->Data4[0]  = (UINT8) Data4[0];
453   GuidBuffer->Data4[1]  = (UINT8) Data4[1];
454   GuidBuffer->Data4[2]  = (UINT8) Data4[2];
455   GuidBuffer->Data4[3]  = (UINT8) Data4[3];
456   GuidBuffer->Data4[4]  = (UINT8) Data4[4];
457   GuidBuffer->Data4[5]  = (UINT8) Data4[5];
458   GuidBuffer->Data4[6]  = (UINT8) Data4[6];
459   GuidBuffer->Data4[7]  = (UINT8) Data4[7];
460 
461   return EFI_SUCCESS;
462 }
463 
464 EFI_STATUS
AsciiStringToUint64(IN CONST CHAR8 * AsciiString,IN BOOLEAN IsHex,OUT UINT64 * ReturnValue)465 AsciiStringToUint64 (
466   IN CONST CHAR8  *AsciiString,
467   IN BOOLEAN      IsHex,
468   OUT UINT64      *ReturnValue
469   )
470 /*++
471 
472 Routine Description:
473 
474   Converts a null terminated ascii string that represents a number into a
475   UINT64 value.  A hex number may be preceded by a 0x, but may not be
476   succeeded by an h.  A number without 0x or 0X is considered to be base 10
477   unless the IsHex input is true.
478 
479 Arguments:
480 
481   AsciiString   The string to convert.
482   IsHex         Force the string to be treated as a hex number.
483   ReturnValue   The return value.
484 
485 Returns:
486 
487   EFI_SUCCESS   Number successfully converted.
488   EFI_ABORTED   Invalid character encountered.
489 
490 --*/
491 {
492   UINT8   Index;
493   UINT64  Value;
494   CHAR8   CurrentChar;
495 
496   //
497   // Initialize the result
498   //
499   Value = 0;
500   Index = 0;
501 
502   //
503   // Check input parameter
504   //
505   if (AsciiString == NULL || ReturnValue == NULL || strlen(AsciiString) > 0xFF) {
506     return EFI_INVALID_PARAMETER;
507   }
508   while (AsciiString[Index] == ' ') {
509     Index ++;
510   }
511 
512   //
513   // Add each character to the result
514   //
515 
516   //
517   // Skip first two chars only if the string starts with '0x' or '0X'
518   //
519   if (AsciiString[Index] == '0' && (AsciiString[Index + 1] == 'x' || AsciiString[Index + 1] == 'X')) {
520     IsHex = TRUE;
521     Index += 2;
522   }
523   if (IsHex) {
524     //
525     // Convert the hex string.
526     //
527     for (; AsciiString[Index] != '\0'; Index++) {
528       CurrentChar = AsciiString[Index];
529       if (CurrentChar == ' ') {
530         break;
531       }
532       //
533       // Verify Hex string
534       //
535       if (isxdigit ((int)CurrentChar) == 0) {
536         return EFI_ABORTED;
537       }
538       //
539       // Add hex value
540       //
541       Value *= 16;
542       if (CurrentChar >= '0' && CurrentChar <= '9') {
543         Value += CurrentChar - '0';
544       } else if (CurrentChar >= 'a' && CurrentChar <= 'f') {
545         Value += CurrentChar - 'a' + 10;
546       } else if (CurrentChar >= 'A' && CurrentChar <= 'F') {
547         Value += CurrentChar - 'A' + 10;
548       }
549     }
550 
551     *ReturnValue = Value;
552   } else {
553     //
554     // Convert dec string is a number
555     //
556     for (; Index < strlen (AsciiString); Index++) {
557       CurrentChar = AsciiString[Index];
558       if (CurrentChar == ' ') {
559         break;
560       }
561       //
562       // Verify Dec string
563       //
564       if (isdigit ((int)CurrentChar) == 0) {
565         return EFI_ABORTED;
566       }
567       //
568       // Add dec value
569       //
570       Value = Value * 10;
571       Value += CurrentChar - '0';
572     }
573 
574     *ReturnValue = Value;
575   }
576 
577   return EFI_SUCCESS;
578 }
579 
580 CHAR8 *
ReadLineInStream(IN FILE * InputFile,IN OUT CHAR8 * InputBuffer)581 ReadLineInStream (
582   IN FILE       *InputFile,
583   IN OUT CHAR8  *InputBuffer
584   )
585 /*++
586 
587 Routine Description:
588 
589   This function reads a line, stripping any comments.
590   // BUGBUG:  This is obsolete once genmake goes away...
591 
592 Arguments:
593 
594   InputFile     Stream pointer.
595   InputBuffer   Buffer to read into, must be MAX_LONG_FILE_PATH size.
596 
597 Returns:
598 
599   NULL if error or EOF
600   InputBuffer otherwise
601 
602 --*/
603 {
604   CHAR8 *CharPtr;
605 
606   //
607   // Verify input parameters are not null
608   //
609   assert (InputFile);
610   assert (InputBuffer);
611 
612   //
613   // Read a line
614   //
615   if (fgets (InputBuffer, MAX_LONG_FILE_PATH, InputFile) == NULL) {
616     return NULL;
617   }
618   //
619   // Strip any comments
620   //
621   CharPtr = strstr (InputBuffer, "//");
622   if (CharPtr != 0) {
623     CharPtr[0] = 0;
624   }
625 
626   CharPtr = strstr (InputBuffer, "#");
627   if (CharPtr != 0) {
628     CharPtr[0] = 0;
629   }
630   //
631   // Return the string
632   //
633   return InputBuffer;
634 }
635 
636 BOOLEAN
FindSectionInStream(IN FILE * InputFile,IN CHAR8 * Section)637 FindSectionInStream (
638   IN FILE       *InputFile,
639   IN CHAR8      *Section
640   )
641 /*++
642 
643 Routine Description:
644 
645   This function parses a stream file from the beginning to find a section.
646   The section string may be anywhere within a line.
647   // BUGBUG:  This is obsolete once genmake goes away...
648 
649 Arguments:
650 
651   InputFile     Stream pointer.
652   Section       Section to search for
653 
654 Returns:
655 
656   FALSE if error or EOF
657   TRUE if section found
658 
659 --*/
660 {
661   CHAR8 InputBuffer[MAX_LONG_FILE_PATH];
662   CHAR8 *CurrentToken;
663 
664   //
665   // Verify input is not NULL
666   //
667   assert (InputFile);
668   assert (Section);
669 
670   //
671   // Rewind to beginning of file
672   //
673   if (fseek (InputFile, 0, SEEK_SET) != 0) {
674     return FALSE;
675   }
676   //
677   // Read lines until the section is found
678   //
679   while (feof (InputFile) == 0) {
680     //
681     // Read a line
682     //
683     ReadLineInStream (InputFile, InputBuffer);
684 
685     //
686     // Check if the section is found
687     //
688     CurrentToken = strstr (InputBuffer, Section);
689     if (CurrentToken != NULL) {
690       return TRUE;
691     }
692   }
693 
694   return FALSE;
695 }
696