1 /** @file
2 Generic but simple file parsing routines.
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 <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 
14 #include "CommonLib.h"
15 #include "EfiUtilityMsgs.h"
16 #include "SimpleFileParsing.h"
17 
18 #ifndef MAX_PATH
19 #define MAX_PATH  255
20 #endif
21 //
22 // just in case we get in an endless loop.
23 //
24 #define MAX_NEST_DEPTH  20
25 //
26 // number of wchars
27 //
28 #define MAX_STRING_IDENTIFIER_NAME  100
29 
30 #define T_CHAR_SPACE                ' '
31 #define T_CHAR_NULL                 0
32 #define T_CHAR_CR                   '\r'
33 #define T_CHAR_TAB                  '\t'
34 #define T_CHAR_LF                   '\n'
35 #define T_CHAR_SLASH                '/'
36 #define T_CHAR_BACKSLASH            '\\'
37 #define T_CHAR_DOUBLE_QUOTE         '"'
38 #define T_CHAR_LC_X                 'x'
39 #define T_CHAR_0                    '0'
40 #define T_CHAR_STAR                 '*'
41 
42 //
43 // We keep a linked list of these for the source files we process
44 //
45 typedef struct _SOURCE_FILE {
46   FILE                *Fptr;
47   CHAR8               *FileBuffer;
48   CHAR8               *FileBufferPtr;
49   UINTN               FileSize;
50   CHAR8               FileName[MAX_PATH];
51   UINTN               LineNum;
52   BOOLEAN             EndOfFile;
53   BOOLEAN             SkipToHash;
54   struct _SOURCE_FILE *Previous;
55   struct _SOURCE_FILE *Next;
56   CHAR8               ControlCharacter;
57 } SOURCE_FILE;
58 
59 typedef struct {
60   CHAR8   *FileBufferPtr;
61 } FILE_POSITION;
62 
63 //
64 // Keep all our module globals in this structure
65 //
66 STATIC struct {
67   SOURCE_FILE SourceFile;
68   BOOLEAN     VerboseFile;
69   BOOLEAN     VerboseToken;
70 } mGlobals;
71 
72 STATIC
73 UINTN
74 t_strcmp (
75   CHAR8  *Buffer,
76   CHAR8  *Str
77   );
78 
79 STATIC
80 UINTN
81 t_strncmp (
82   CHAR8  *Str1,
83   CHAR8  *Str2,
84   INTN    Len
85   );
86 
87 STATIC
88 UINTN
89 t_strlen (
90   CHAR8  *Str
91   );
92 
93 STATIC
94 VOID
95 RewindFile (
96   SOURCE_FILE *SourceFile
97   );
98 
99 STATIC
100 BOOLEAN
101 IsWhiteSpace (
102   SOURCE_FILE *SourceFile
103   );
104 
105 STATIC
106 UINTN
107 SkipWhiteSpace (
108   SOURCE_FILE *SourceFile
109   );
110 
111 STATIC
112 BOOLEAN
113 EndOfFile (
114   SOURCE_FILE *SourceFile
115   );
116 
117 STATIC
118 VOID
119 PreprocessFile (
120   SOURCE_FILE *SourceFile
121   );
122 
123 STATIC
124 CHAR8   *
125 t_strcpy (
126   CHAR8  *Dest,
127   CHAR8  *Src
128   );
129 
130 STATIC
131 STATUS
132 ProcessIncludeFile (
133   SOURCE_FILE *SourceFile,
134   SOURCE_FILE *ParentSourceFile
135   );
136 
137 STATIC
138 STATUS
139 ProcessFile (
140   SOURCE_FILE *SourceFile
141   );
142 
143 STATIC
144 STATUS
145 GetFilePosition (
146   FILE_POSITION *Fpos
147   );
148 
149 STATIC
150 STATUS
151 SetFilePosition (
152   FILE_POSITION *Fpos
153   );
154 
155 STATUS
SFPInit(VOID)156 SFPInit (
157   VOID
158   )
159 /*++
160 
161 Routine Description:
162 
163 Arguments:
164   None.
165 
166 Returns:
167   STATUS_SUCCESS always
168 
169 --*/
170 {
171   memset ((VOID *) &mGlobals, 0, sizeof (mGlobals));
172   return STATUS_SUCCESS;
173 }
174 
175 UINTN
SFPGetLineNumber(VOID)176 SFPGetLineNumber (
177   VOID
178   )
179 /*++
180 
181 Routine Description:
182   Return the line number of the file we're parsing. Used
183   for error reporting purposes.
184 
185 Arguments:
186   None.
187 
188 Returns:
189   The line number, or 0 if no file is being processed
190 
191 --*/
192 {
193   return mGlobals.SourceFile.LineNum;
194 }
195 
196 CHAR8  *
SFPGetFileName(VOID)197 SFPGetFileName (
198   VOID
199   )
200 /*++
201 
202 Routine Description:
203   Return the name of the file we're parsing. Used
204   for error reporting purposes.
205 
206 Arguments:
207   None.
208 
209 Returns:
210   A pointer to the file name. Null if no file is being
211   processed.
212 
213 --*/
214 {
215   if (mGlobals.SourceFile.FileName[0]) {
216     return mGlobals.SourceFile.FileName;
217   }
218 
219   return NULL;
220 }
221 
222 STATUS
SFPOpenFile(CHAR8 * FileName)223 SFPOpenFile (
224   CHAR8      *FileName
225   )
226 /*++
227 
228 Routine Description:
229   Open a file for parsing.
230 
231 Arguments:
232   FileName  - name of the file to parse
233 
234 Returns:
235 
236 
237 --*/
238 {
239   STATUS  Status;
240   t_strcpy (mGlobals.SourceFile.FileName, FileName);
241   Status = ProcessIncludeFile (&mGlobals.SourceFile, NULL);
242   return Status;
243 }
244 
245 BOOLEAN
SFPIsToken(CHAR8 * Str)246 SFPIsToken (
247   CHAR8  *Str
248   )
249 /*++
250 
251 Routine Description:
252   Check to see if the specified token is found at
253   the current position in the input file.
254 
255 Arguments:
256   Str - the token to look for
257 
258 Returns:
259   TRUE - the token is next
260   FALSE - the token is not next
261 
262 Notes:
263   We do a simple string comparison on this function. It is
264   the responsibility of the caller to ensure that the token
265   is not a subset of some other token.
266 
267   The file pointer is advanced past the token in the input file.
268 
269 --*/
270 {
271   UINTN  Len;
272   SkipWhiteSpace (&mGlobals.SourceFile);
273   if (EndOfFile (&mGlobals.SourceFile)) {
274     return FALSE;
275   }
276 
277   if ((Len = t_strcmp (mGlobals.SourceFile.FileBufferPtr, Str)) > 0) {
278     mGlobals.SourceFile.FileBufferPtr += Len;
279     if (mGlobals.VerboseToken) {
280       printf ("Token: '%s'\n", Str);
281     }
282 
283     return TRUE;
284   }
285 
286   return FALSE;
287 }
288 
289 BOOLEAN
SFPIsKeyword(CHAR8 * Str)290 SFPIsKeyword (
291   CHAR8  *Str
292   )
293 /*++
294 
295 Routine Description:
296   Check to see if the specified keyword is found at
297   the current position in the input file.
298 
299 Arguments:
300   Str - keyword to look for
301 
302 Returns:
303   TRUE - the keyword is next
304   FALSE - the keyword is not next
305 
306 Notes:
307   A keyword is defined as a "special" string that has a non-alphanumeric
308   character following it.
309 
310 --*/
311 {
312   UINTN  Len;
313   SkipWhiteSpace (&mGlobals.SourceFile);
314   if (EndOfFile (&mGlobals.SourceFile)) {
315     return FALSE;
316   }
317 
318   if ((Len = t_strcmp (mGlobals.SourceFile.FileBufferPtr, Str)) > 0) {
319     if (isalnum ((int)mGlobals.SourceFile.FileBufferPtr[Len])) {
320       return FALSE;
321     }
322 
323     mGlobals.SourceFile.FileBufferPtr += Len;
324     if (mGlobals.VerboseToken) {
325       printf ("Token: '%s'\n", Str);
326     }
327 
328     return TRUE;
329   }
330 
331   return FALSE;
332 }
333 
334 BOOLEAN
SFPGetNextToken(CHAR8 * Str,UINTN Len)335 SFPGetNextToken (
336   CHAR8  *Str,
337   UINTN  Len
338   )
339 /*++
340 
341 Routine Description:
342   Get the next token from the input stream.
343 
344 Arguments:
345   Str - pointer to a copy of the next token
346   Len - size of buffer pointed to by Str
347 
348 Returns:
349   TRUE  - next token successfully returned
350   FALSE - otherwise
351 
352 Notes:
353   Preceding white space is ignored.
354   The parser's buffer pointer is advanced past the end of the
355   token.
356 
357 --*/
358 {
359   UINTN  Index;
360   CHAR8  TempChar;
361 
362   SkipWhiteSpace (&mGlobals.SourceFile);
363   if (EndOfFile (&mGlobals.SourceFile)) {
364     return FALSE;
365   }
366   //
367   // Have to have enough string for at least one char and a null-terminator
368   //
369   if (Len < 2) {
370     return FALSE;
371   }
372   //
373   // Look at the first character. If it's an identifier, then treat it
374   // as such
375   //
376   TempChar = mGlobals.SourceFile.FileBufferPtr[0];
377   if (((TempChar >= 'a') && (TempChar <= 'z')) || ((TempChar >= 'A') && (TempChar <= 'Z')) || (TempChar == '_')) {
378     Str[0] = TempChar;
379     mGlobals.SourceFile.FileBufferPtr++;
380     Index = 1;
381     while (!EndOfFile (&mGlobals.SourceFile) && (Index < Len)) {
382       TempChar = mGlobals.SourceFile.FileBufferPtr[0];
383       if (((TempChar >= 'a') && (TempChar <= 'z')) ||
384           ((TempChar >= 'A') && (TempChar <= 'Z')) ||
385           ((TempChar >= '0') && (TempChar <= '9')) ||
386           (TempChar == '_')
387           ) {
388         Str[Index] = mGlobals.SourceFile.FileBufferPtr[0];
389         mGlobals.SourceFile.FileBufferPtr++;
390         Index++;
391       } else {
392         //
393         // Invalid character for symbol name, so break out
394         //
395         break;
396       }
397     }
398     //
399     // Null terminate and return success
400     //
401     Str[Index] = 0;
402     return TRUE;
403   } else if ((TempChar == ')') || (TempChar == '(') || (TempChar == '*')) {
404     Str[0] = mGlobals.SourceFile.FileBufferPtr[0];
405     mGlobals.SourceFile.FileBufferPtr++;
406     Str[1] = 0;
407     return TRUE;
408   } else {
409     //
410     // Everything else is white-space (or EOF) separated
411     //
412     Index = 0;
413     while (!EndOfFile (&mGlobals.SourceFile) && (Index < Len)) {
414       if (IsWhiteSpace (&mGlobals.SourceFile)) {
415         if (Index > 0) {
416           Str[Index] = 0;
417           return TRUE;
418         }
419 
420         return FALSE;
421       } else {
422         Str[Index] = mGlobals.SourceFile.FileBufferPtr[0];
423         mGlobals.SourceFile.FileBufferPtr++;
424         Index++;
425       }
426     }
427     //
428     // See if we just ran out of file contents, but did find a token
429     //
430     if ((Index > 0) && EndOfFile (&mGlobals.SourceFile)) {
431       Str[Index] = 0;
432       return TRUE;
433     }
434   }
435 
436   return FALSE;
437 }
438 
439 BOOLEAN
SFPGetGuidToken(CHAR8 * Str,UINT32 Len)440 SFPGetGuidToken (
441   CHAR8  *Str,
442   UINT32 Len
443   )
444 /*++
445 
446 Routine Description:
447   Parse a GUID from the input stream. Stop when you discover white space.
448 
449 Arguments:
450   Str - pointer to a copy of the next token
451   Len - size of buffer pointed to by Str
452 
453 Returns:
454   TRUE  - GUID string returned successfully
455   FALSE - otherwise
456 
457 --*/
458 {
459   UINT32  Index;
460   SkipWhiteSpace (&mGlobals.SourceFile);
461   if (EndOfFile (&mGlobals.SourceFile)) {
462     return FALSE;
463   }
464 
465   Index = 0;
466   while (!EndOfFile (&mGlobals.SourceFile) && (Index < Len)) {
467     if (IsWhiteSpace (&mGlobals.SourceFile)) {
468       if (Index > 0) {
469         Str[Index] = 0;
470         return TRUE;
471       }
472 
473       return FALSE;
474     } else {
475       Str[Index] = mGlobals.SourceFile.FileBufferPtr[0];
476       mGlobals.SourceFile.FileBufferPtr++;
477       Index++;
478     }
479   }
480 
481   return FALSE;
482 }
483 
484 BOOLEAN
SFPSkipToToken(CHAR8 * Str)485 SFPSkipToToken (
486   CHAR8  *Str
487   )
488 {
489   UINTN  Len;
490   CHAR8         *SavePos;
491   Len     = t_strlen (Str);
492   SavePos = mGlobals.SourceFile.FileBufferPtr;
493   SkipWhiteSpace (&mGlobals.SourceFile);
494   while (!EndOfFile (&mGlobals.SourceFile)) {
495     if (t_strncmp (Str, mGlobals.SourceFile.FileBufferPtr, Len) == 0) {
496       mGlobals.SourceFile.FileBufferPtr += Len;
497       return TRUE;
498     }
499 
500     mGlobals.SourceFile.FileBufferPtr++;
501     SkipWhiteSpace (&mGlobals.SourceFile);
502   }
503 
504   mGlobals.SourceFile.FileBufferPtr = SavePos;
505   return FALSE;
506 }
507 
508 BOOLEAN
SFPGetNumber(UINTN * Value)509 SFPGetNumber (
510   UINTN *Value
511   )
512 /*++
513 
514 Routine Description:
515   Check the token at the current file position for a numeric value.
516   May be either decimal or hex.
517 
518 Arguments:
519   Value  - pointer where to store the value
520 
521 Returns:
522   FALSE    - current token is not a number
523   TRUE     - current token is a number
524 
525 --*/
526 {
527   int Val;
528 
529   SkipWhiteSpace (&mGlobals.SourceFile);
530   if (EndOfFile (&mGlobals.SourceFile)) {
531     return FALSE;
532   }
533 
534   if (isdigit ((int)mGlobals.SourceFile.FileBufferPtr[0])) {
535     //
536     // Check for hex value
537     //
538     if ((mGlobals.SourceFile.FileBufferPtr[0] == T_CHAR_0) && (mGlobals.SourceFile.FileBufferPtr[1] == T_CHAR_LC_X)) {
539       if (!isxdigit ((int)mGlobals.SourceFile.FileBufferPtr[2])) {
540         return FALSE;
541       }
542 
543       mGlobals.SourceFile.FileBufferPtr += 2;
544       sscanf (mGlobals.SourceFile.FileBufferPtr, "%x", &Val);
545       *Value = (UINT32) Val;
546       while (isxdigit ((int)mGlobals.SourceFile.FileBufferPtr[0])) {
547         mGlobals.SourceFile.FileBufferPtr++;
548       }
549 
550       return TRUE;
551     } else {
552       *Value = atoi (mGlobals.SourceFile.FileBufferPtr);
553       while (isdigit ((int)mGlobals.SourceFile.FileBufferPtr[0])) {
554         mGlobals.SourceFile.FileBufferPtr++;
555       }
556 
557       return TRUE;
558     }
559   } else {
560     return FALSE;
561   }
562 }
563 
564 STATUS
SFPCloseFile(VOID)565 SFPCloseFile (
566   VOID
567   )
568 /*++
569 
570 Routine Description:
571   Close the file being parsed.
572 
573 Arguments:
574   None.
575 
576 Returns:
577   STATUS_SUCCESS - the file was closed
578   STATUS_ERROR   - no file is currently open
579 
580 --*/
581 {
582   if (mGlobals.SourceFile.FileBuffer != NULL) {
583     free (mGlobals.SourceFile.FileBuffer);
584     memset (&mGlobals.SourceFile, 0, sizeof (mGlobals.SourceFile));
585     return STATUS_SUCCESS;
586   }
587 
588   return STATUS_ERROR;
589 }
590 
591 STATIC
592 STATUS
ProcessIncludeFile(SOURCE_FILE * SourceFile,SOURCE_FILE * ParentSourceFile)593 ProcessIncludeFile (
594   SOURCE_FILE *SourceFile,
595   SOURCE_FILE *ParentSourceFile
596   )
597 /*++
598 
599 Routine Description:
600 
601   Given a source file, open the file and parse it
602 
603 Arguments:
604 
605   SourceFile        - name of file to parse
606   ParentSourceFile  - for error reporting purposes, the file that #included SourceFile.
607 
608 Returns:
609 
610   Standard status.
611 
612 --*/
613 {
614   STATIC UINTN NestDepth = 0;
615   CHAR8               FoundFileName[MAX_PATH];
616   STATUS              Status;
617 
618   Status = STATUS_SUCCESS;
619   NestDepth++;
620   //
621   // Print the file being processed. Indent so you can tell the include nesting
622   // depth.
623   //
624   if (mGlobals.VerboseFile) {
625     fprintf (stdout, "%*cProcessing file '%s'\n", (int)NestDepth * 2, ' ', SourceFile->FileName);
626     fprintf (stdout, "Parent source file = '%s'\n", ParentSourceFile->FileName);
627   }
628 
629   //
630   // Make sure we didn't exceed our maximum nesting depth
631   //
632   if (NestDepth > MAX_NEST_DEPTH) {
633     Error (NULL, 0, 3001, "Not Supported", "%s exceeds max nesting depth (%u)", SourceFile->FileName, (unsigned) NestDepth);
634     Status = STATUS_ERROR;
635     goto Finish;
636   }
637   //
638   // Try to open the file locally, and if that fails try along our include paths.
639   //
640   strcpy (FoundFileName, SourceFile->FileName);
641   if ((SourceFile->Fptr = fopen (LongFilePath (FoundFileName), "rb")) == NULL) {
642     return STATUS_ERROR;
643   }
644   //
645   // Process the file found
646   //
647   ProcessFile (SourceFile);
648 Finish:
649   //
650   // Close open files and return status
651   //
652   if (SourceFile->Fptr != NULL) {
653     fclose (SourceFile->Fptr);
654     SourceFile->Fptr = NULL;
655   }
656 
657   return Status;
658 }
659 
660 STATIC
661 STATUS
ProcessFile(SOURCE_FILE * SourceFile)662 ProcessFile (
663   SOURCE_FILE *SourceFile
664   )
665 /*++
666 
667 Routine Description:
668 
669   Given a source file that's been opened, read the contents into an internal
670   buffer and pre-process it to remove comments.
671 
672 Arguments:
673 
674   SourceFile        - structure containing info on the file to process
675 
676 Returns:
677 
678   Standard status.
679 
680 --*/
681 {
682   //
683   // Get the file size, and then read the entire thing into memory.
684   // Allocate extra space for a terminator character.
685   //
686   fseek (SourceFile->Fptr, 0, SEEK_END);
687   SourceFile->FileSize = ftell (SourceFile->Fptr);
688   if (mGlobals.VerboseFile) {
689     printf ("FileSize = %u (0x%X)\n", (unsigned) SourceFile->FileSize, (unsigned) SourceFile->FileSize);
690   }
691 
692   fseek (SourceFile->Fptr, 0, SEEK_SET);
693   SourceFile->FileBuffer = (CHAR8  *) malloc (SourceFile->FileSize + sizeof (CHAR8 ));
694   if (SourceFile->FileBuffer == NULL) {
695     Error (NULL, 0, 4001, "Resource: memory cannot be allocated", NULL);
696     return STATUS_ERROR;
697   }
698 
699   fread ((VOID *) SourceFile->FileBuffer, SourceFile->FileSize, 1, SourceFile->Fptr);
700   SourceFile->FileBuffer[(SourceFile->FileSize / sizeof (CHAR8 ))] = T_CHAR_NULL;
701   //
702   // Pre-process the file to replace comments with spaces
703   //
704   PreprocessFile (SourceFile);
705   SourceFile->LineNum = 1;
706   return STATUS_SUCCESS;
707 }
708 
709 STATIC
710 VOID
PreprocessFile(SOURCE_FILE * SourceFile)711 PreprocessFile (
712   SOURCE_FILE *SourceFile
713   )
714 /*++
715 
716 Routine Description:
717   Preprocess a file to replace all carriage returns with NULLs so
718   we can print lines (as part of error messages) from the file to the screen.
719 
720 Arguments:
721   SourceFile - structure that we use to keep track of an input file.
722 
723 Returns:
724   Nothing.
725 
726 --*/
727 {
728   BOOLEAN InComment;
729   BOOLEAN SlashSlashComment;
730   int     LineNum;
731 
732   RewindFile (SourceFile);
733   InComment         = FALSE;
734   SlashSlashComment = FALSE;
735   while (!EndOfFile (SourceFile)) {
736     //
737     // If a line-feed, then no longer in a comment if we're in a // comment
738     //
739     if (SourceFile->FileBufferPtr[0] == T_CHAR_LF) {
740       SourceFile->FileBufferPtr++;
741       SourceFile->LineNum++;
742       if (InComment && SlashSlashComment) {
743         InComment         = FALSE;
744         SlashSlashComment = FALSE;
745       }
746     } else if (SourceFile->FileBufferPtr[0] == T_CHAR_CR) {
747       //
748       // Replace all carriage returns with a NULL so we can print stuff
749       //
750       SourceFile->FileBufferPtr[0] = 0;
751       SourceFile->FileBufferPtr++;
752       //
753       // Check for */ comment end
754       //
755     } else if (InComment &&
756              !SlashSlashComment &&
757              (SourceFile->FileBufferPtr[0] == T_CHAR_STAR) &&
758              (SourceFile->FileBufferPtr[1] == T_CHAR_SLASH)
759             ) {
760       SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
761       SourceFile->FileBufferPtr++;
762       SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
763       SourceFile->FileBufferPtr++;
764       InComment = FALSE;
765     } else if (InComment) {
766       SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
767       SourceFile->FileBufferPtr++;
768       //
769       // Check for // comments
770       //
771     } else if ((SourceFile->FileBufferPtr[0] == T_CHAR_SLASH) && (SourceFile->FileBufferPtr[1] == T_CHAR_SLASH)) {
772       InComment         = TRUE;
773       SlashSlashComment = TRUE;
774       //
775       // Check for /* comment start
776       //
777     } else if ((SourceFile->FileBufferPtr[0] == T_CHAR_SLASH) && (SourceFile->FileBufferPtr[1] == T_CHAR_STAR)) {
778       SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
779       SourceFile->FileBufferPtr++;
780       SourceFile->FileBufferPtr[0] = T_CHAR_SPACE;
781       SourceFile->FileBufferPtr++;
782       SlashSlashComment = FALSE;
783       InComment         = TRUE;
784     } else {
785       SourceFile->FileBufferPtr++;
786     }
787   }
788   //
789   // Could check for end-of-file and still in a comment, but
790   // should not be necessary. So just restore the file pointers.
791   //
792   RewindFile (SourceFile);
793   //
794   // Dump the reformatted file if verbose mode
795   //
796   if (mGlobals.VerboseFile) {
797     LineNum = 1;
798     printf ("%04d: ", LineNum);
799     while (!EndOfFile (SourceFile)) {
800       if (SourceFile->FileBufferPtr[0] == T_CHAR_LF) {
801         printf ("'\n%04d: '", ++LineNum);
802       } else {
803         printf ("%c", SourceFile->FileBufferPtr[0]);
804       }
805 
806       SourceFile->FileBufferPtr++;
807     }
808 
809     printf ("'\n");
810     printf ("FileSize = %u (0x%X)\n", (unsigned)SourceFile->FileSize, (unsigned)SourceFile->FileSize);
811     RewindFile (SourceFile);
812   }
813 }
814 
815 BOOLEAN
SFPGetQuotedString(CHAR8 * Str,INTN Length)816 SFPGetQuotedString (
817   CHAR8       *Str,
818   INTN         Length
819   )
820 /*++
821 
822 Routine Description:
823   Retrieve a quoted-string from the input file.
824 
825 Arguments:
826   Str    - pointer to a copy of the quoted string parsed
827   Length - size of buffer pointed to by Str
828 
829 Returns:
830   TRUE    - next token in input stream was a quoted string, and
831             the string value was returned in Str
832   FALSE   - otherwise
833 
834 --*/
835 {
836   SkipWhiteSpace (&mGlobals.SourceFile);
837   if (EndOfFile (&mGlobals.SourceFile)) {
838     return FALSE;
839   }
840 
841   if (mGlobals.SourceFile.FileBufferPtr[0] == T_CHAR_DOUBLE_QUOTE) {
842     mGlobals.SourceFile.FileBufferPtr++;
843     while (Length > 0) {
844       if (EndOfFile (&mGlobals.SourceFile)) {
845         return FALSE;
846       }
847       //
848       // Check for closing quote
849       //
850       if (mGlobals.SourceFile.FileBufferPtr[0] == T_CHAR_DOUBLE_QUOTE) {
851         mGlobals.SourceFile.FileBufferPtr++;
852         *Str = 0;
853         return TRUE;
854       }
855 
856       *Str = mGlobals.SourceFile.FileBufferPtr[0];
857       Str++;
858       Length--;
859       mGlobals.SourceFile.FileBufferPtr++;
860     }
861   }
862   //
863   // First character was not a quote, or the input string length was
864   // insufficient to contain the quoted string, so return failure code.
865   //
866   return FALSE;
867 }
868 
869 BOOLEAN
SFPIsEOF(VOID)870 SFPIsEOF (
871   VOID
872   )
873 /*++
874 
875 Routine Description:
876   Return TRUE of FALSE to indicate whether or not we've reached the end of the
877   file we're parsing.
878 
879 Arguments:
880   NA
881 
882 Returns:
883   TRUE    - EOF reached
884   FALSE   - otherwise
885 
886 --*/
887 {
888   SkipWhiteSpace (&mGlobals.SourceFile);
889   return EndOfFile (&mGlobals.SourceFile);
890 }
891 
892 #if 0
893 STATIC
894 CHAR8  *
895 GetQuotedString (
896   SOURCE_FILE *SourceFile,
897   BOOLEAN     Optional
898   )
899 {
900   CHAR8         *String;
901   CHAR8         *Start;
902   CHAR8         *Ptr;
903   UINTN         Len;
904   BOOLEAN       PreviousBackslash;
905 
906   if (SourceFile->FileBufferPtr[0] != T_CHAR_DOUBLE_QUOTE) {
907     if (Optional == FALSE) {
908       Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted string", "%S", SourceFile->FileBufferPtr);
909     }
910 
911     return NULL;
912   }
913 
914   Len = 0;
915   SourceFile->FileBufferPtr++;
916   Start             = Ptr = SourceFile->FileBufferPtr;
917   PreviousBackslash = FALSE;
918   while (!EndOfFile (SourceFile)) {
919     if ((SourceFile->FileBufferPtr[0] == T_CHAR_DOUBLE_QUOTE) && (PreviousBackslash == FALSE)) {
920       break;
921     } else if (SourceFile->FileBufferPtr[0] == T_CHAR_CR) {
922       Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start);
923       PreviousBackslash = FALSE;
924     } else if (SourceFile->FileBufferPtr[0] == T_CHAR_BACKSLASH) {
925       PreviousBackslash = TRUE;
926     } else {
927       PreviousBackslash = FALSE;
928     }
929 
930     SourceFile->FileBufferPtr++;
931     Len++;
932   }
933 
934   if (SourceFile->FileBufferPtr[0] != T_CHAR_DOUBLE_QUOTE) {
935     Warning (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing quote on string", "%S", Start);
936   } else {
937     SourceFile->FileBufferPtr++;
938   }
939   //
940   // Now allocate memory for the string and save it off
941   //
942   String = (CHAR8  *) malloc ((Len + 1) * sizeof (CHAR8 ));
943   if (String == NULL) {
944     Error (NULL, 0, 4001, "Resource: memory cannot be allocated", NULL);
945     return NULL;
946   }
947   //
948   // Copy the string from the file buffer to the local copy.
949   // We do no reformatting of it whatsoever at this point.
950   //
951   Ptr = String;
952   while (Len > 0) {
953     *Ptr = *Start;
954     Start++;
955     Ptr++;
956     Len--;
957   }
958 
959   *Ptr = 0;
960   return String;
961 }
962 #endif
963 STATIC
964 BOOLEAN
EndOfFile(SOURCE_FILE * SourceFile)965 EndOfFile (
966   SOURCE_FILE *SourceFile
967   )
968 {
969   //
970   // The file buffer pointer will typically get updated before the End-of-file flag in the
971   // source file structure, so check it first.
972   //
973   if (SourceFile->FileBufferPtr >= SourceFile->FileBuffer + SourceFile->FileSize / sizeof (CHAR8 )) {
974     SourceFile->EndOfFile = TRUE;
975     return TRUE;
976   }
977 
978   if (SourceFile->EndOfFile) {
979     return TRUE;
980   }
981 
982   return FALSE;
983 }
984 
985 #if 0
986 STATIC
987 VOID
988 ProcessTokenInclude (
989   SOURCE_FILE *SourceFile
990   )
991 {
992   CHAR8          IncludeFileName[MAX_PATH];
993   CHAR8          *To;
994   UINTN  Len;
995   BOOLEAN       ReportedError;
996   SOURCE_FILE   IncludedSourceFile;
997 
998   ReportedError = FALSE;
999   if (SkipWhiteSpace (SourceFile) == 0) {
1000     Warning (SourceFile->FileName, SourceFile->LineNum, 0, "expected whitespace following #include keyword", NULL);
1001   }
1002   //
1003   // Should be quoted file name
1004   //
1005   if (SourceFile->FileBufferPtr[0] != T_CHAR_DOUBLE_QUOTE) {
1006     Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted include file name", NULL);
1007     goto FailDone;
1008   }
1009 
1010   SourceFile->FileBufferPtr++;
1011   //
1012   // Copy the filename as ascii to our local string
1013   //
1014   To  = IncludeFileName;
1015   Len = 0;
1016   while (!EndOfFile (SourceFile)) {
1017     if ((SourceFile->FileBufferPtr[0] == T_CHAR_CR) || (SourceFile->FileBufferPtr[0] == T_CHAR_LF)) {
1018       Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-line found in quoted include file name", NULL);
1019       goto FailDone;
1020     }
1021 
1022     if (SourceFile->FileBufferPtr[0] == T_CHAR_DOUBLE_QUOTE) {
1023       SourceFile->FileBufferPtr++;
1024       break;
1025     }
1026     //
1027     // If too long, then report the error once and process until the closing quote
1028     //
1029     Len++;
1030     if (!ReportedError && (Len >= sizeof (IncludeFileName))) {
1031       Error (SourceFile->FileName, SourceFile->LineNum, 0, "length of include file name exceeds limit", NULL);
1032       ReportedError = TRUE;
1033     }
1034 
1035     if (!ReportedError) {
1036       *To = (CHAR8 ) SourceFile->FileBufferPtr[0];
1037       To++;
1038     }
1039 
1040     SourceFile->FileBufferPtr++;
1041   }
1042 
1043   if (!ReportedError) {
1044     *To = 0;
1045     memset ((CHAR8 *) &IncludedSourceFile, 0, sizeof (SOURCE_FILE));
1046     strcpy (IncludedSourceFile.FileName, IncludeFileName);
1047     ProcessIncludeFile (&IncludedSourceFile, SourceFile);
1048   }
1049 
1050   return ;
1051 FailDone:
1052   //
1053   // Error recovery -- skip to next #
1054   //
1055   SourceFile->SkipToHash = TRUE;
1056 }
1057 #endif
1058 STATIC
1059 BOOLEAN
IsWhiteSpace(SOURCE_FILE * SourceFile)1060 IsWhiteSpace (
1061   SOURCE_FILE *SourceFile
1062   )
1063 {
1064   switch (*SourceFile->FileBufferPtr) {
1065   case T_CHAR_NULL:
1066   case T_CHAR_CR:
1067   case T_CHAR_SPACE:
1068   case T_CHAR_TAB:
1069   case T_CHAR_LF:
1070     return TRUE;
1071 
1072   default:
1073     return FALSE;
1074   }
1075 }
1076 
1077 UINTN
SkipWhiteSpace(SOURCE_FILE * SourceFile)1078 SkipWhiteSpace (
1079   SOURCE_FILE *SourceFile
1080   )
1081 {
1082   UINTN  Count;
1083 
1084   Count = 0;
1085   while (!EndOfFile (SourceFile)) {
1086     Count++;
1087     switch (*SourceFile->FileBufferPtr) {
1088     case T_CHAR_NULL:
1089     case T_CHAR_CR:
1090     case T_CHAR_SPACE:
1091     case T_CHAR_TAB:
1092       SourceFile->FileBufferPtr++;
1093       break;
1094 
1095     case T_CHAR_LF:
1096       SourceFile->FileBufferPtr++;
1097       SourceFile->LineNum++;
1098       break;
1099 
1100     default:
1101       return Count - 1;
1102     }
1103   }
1104   //
1105   // Some tokens require trailing whitespace. If we're at the end of the
1106   // file, then we count that as well.
1107   //
1108   if ((Count == 0) && (EndOfFile (SourceFile))) {
1109     Count++;
1110   }
1111 
1112   return Count;
1113 }
1114 
1115 STATIC
1116 UINTN
t_strcmp(CHAR8 * Buffer,CHAR8 * Str)1117 t_strcmp (
1118   CHAR8  *Buffer,
1119   CHAR8  *Str
1120   )
1121 /*++
1122 
1123 Routine Description:
1124   Compare two strings for equality. The string pointed to by 'Buffer' may or may not be null-terminated,
1125   so only compare up to the length of Str.
1126 
1127 Arguments:
1128   Buffer  - pointer to first (possibly not null-terminated) string
1129   Str     - pointer to null-terminated string to compare to Buffer
1130 
1131 Returns:
1132   Number of bytes matched if exact match
1133   0 if Buffer does not start with Str
1134 
1135 --*/
1136 {
1137   UINTN  Len;
1138 
1139   Len = 0;
1140   while (*Str && (*Str == *Buffer)) {
1141     Buffer++;
1142     Str++;
1143     Len++;
1144   }
1145 
1146   if (*Str) {
1147     return 0;
1148   }
1149 
1150   return Len;
1151 }
1152 
1153 STATIC
1154 UINTN
t_strlen(CHAR8 * Str)1155 t_strlen (
1156   CHAR8  *Str
1157   )
1158 {
1159   UINTN  Len;
1160   Len = 0;
1161   while (*Str) {
1162     Len++;
1163     Str++;
1164   }
1165 
1166   return Len;
1167 }
1168 
1169 STATIC
1170 UINTN
t_strncmp(CHAR8 * Str1,CHAR8 * Str2,INTN Len)1171 t_strncmp (
1172   CHAR8  *Str1,
1173   CHAR8  *Str2,
1174   INTN    Len
1175   )
1176 {
1177   while (Len > 0) {
1178     if (*Str1 != *Str2) {
1179       return Len;
1180     }
1181 
1182     Len--;
1183     Str1++;
1184     Str2++;
1185   }
1186 
1187   return 0;
1188 }
1189 
1190 STATIC
1191 CHAR8  *
t_strcpy(CHAR8 * Dest,CHAR8 * Src)1192 t_strcpy (
1193   CHAR8  *Dest,
1194   CHAR8  *Src
1195   )
1196 {
1197   CHAR8   *SaveDest;
1198   SaveDest = Dest;
1199   while (*Src) {
1200     *Dest = *Src;
1201     Dest++;
1202     Src++;
1203   }
1204 
1205   *Dest = 0;
1206   return SaveDest;
1207 }
1208 
1209 STATIC
1210 VOID
RewindFile(SOURCE_FILE * SourceFile)1211 RewindFile (
1212   SOURCE_FILE *SourceFile
1213   )
1214 {
1215   SourceFile->LineNum       = 1;
1216   SourceFile->FileBufferPtr = SourceFile->FileBuffer;
1217   SourceFile->EndOfFile     = 0;
1218 }
1219 
1220 STATIC
1221 UINT32
GetHexChars(CHAR8 * Buffer,UINT32 BufferLen)1222 GetHexChars (
1223   CHAR8       *Buffer,
1224   UINT32      BufferLen
1225   )
1226 {
1227   UINT32  Len;
1228   Len = 0;
1229   while (!EndOfFile (&mGlobals.SourceFile) && (Len < BufferLen)) {
1230     if (isxdigit ((int)mGlobals.SourceFile.FileBufferPtr[0])) {
1231       Buffer[Len] = mGlobals.SourceFile.FileBufferPtr[0];
1232       Len++;
1233       mGlobals.SourceFile.FileBufferPtr++;
1234     } else {
1235       break;
1236     }
1237   }
1238   //
1239   // Null terminate if we can
1240   //
1241   if ((Len > 0) && (Len < BufferLen)) {
1242     Buffer[Len] = 0;
1243   }
1244 
1245   return Len;
1246 }
1247 
1248 BOOLEAN
SFPGetGuid(INTN GuidStyle,EFI_GUID * Value)1249 SFPGetGuid (
1250   INTN         GuidStyle,
1251   EFI_GUID    *Value
1252   )
1253 /*++
1254 
1255 Routine Description:
1256   Parse a GUID from the input stream. Stop when you discover white space.
1257 
1258 Arguments:
1259   GuidStyle - Style of the following GUID token
1260   Value     - pointer to EFI_GUID struct for output
1261 
1262 Returns:
1263   TRUE  - GUID string parsed successfully
1264   FALSE - otherwise
1265 
1266   GUID styles
1267     Style[0] 12345678-1234-5678-AAAA-BBBBCCCCDDDD
1268 
1269 --*/
1270 {
1271   INT32         Value32;
1272   UINT32        Index;
1273   FILE_POSITION FPos;
1274   CHAR8         TempString[20];
1275   CHAR8         TempString2[3];
1276   CHAR8         *From;
1277   CHAR8         *To;
1278   UINT32        Len;
1279   BOOLEAN       Status;
1280 
1281   Status = FALSE;
1282   //
1283   // Skip white space, then start parsing
1284   //
1285   SkipWhiteSpace (&mGlobals.SourceFile);
1286   GetFilePosition (&FPos);
1287   if (EndOfFile (&mGlobals.SourceFile)) {
1288     return FALSE;
1289   }
1290 
1291   if (GuidStyle == PARSE_GUID_STYLE_5_FIELDS) {
1292     //
1293     // Style[0] 12345678-1234-5678-AAAA-BBBBCCCCDDDD
1294     //
1295     Len = GetHexChars (TempString, sizeof (TempString));
1296     if ((Len == 0) || (Len > 8)) {
1297       goto Done;
1298     }
1299 
1300     sscanf (TempString, "%x", &Value32);
1301     Value->Data1 = Value32;
1302     //
1303     // Next two UINT16 fields
1304     //
1305     if (mGlobals.SourceFile.FileBufferPtr[0] != '-') {
1306       goto Done;
1307     }
1308 
1309     mGlobals.SourceFile.FileBufferPtr++;
1310     Len = GetHexChars (TempString, sizeof (TempString));
1311     if ((Len == 0) || (Len > 4)) {
1312       goto Done;
1313     }
1314 
1315     sscanf (TempString, "%x", &Value32);
1316     Value->Data2 = (UINT16) Value32;
1317 
1318     if (mGlobals.SourceFile.FileBufferPtr[0] != '-') {
1319       goto Done;
1320     }
1321 
1322     mGlobals.SourceFile.FileBufferPtr++;
1323     Len = GetHexChars (TempString, sizeof (TempString));
1324     if ((Len == 0) || (Len > 4)) {
1325       goto Done;
1326     }
1327 
1328     sscanf (TempString, "%x", &Value32);
1329     Value->Data3 = (UINT16) Value32;
1330     //
1331     // Parse the "AAAA" as two bytes
1332     //
1333     if (mGlobals.SourceFile.FileBufferPtr[0] != '-') {
1334       goto Done;
1335     }
1336 
1337     mGlobals.SourceFile.FileBufferPtr++;
1338     Len = GetHexChars (TempString, sizeof (TempString));
1339     if ((Len == 0) || (Len > 4)) {
1340       goto Done;
1341     }
1342 
1343     sscanf (TempString, "%x", &Value32);
1344     Value->Data4[0] = (UINT8) (Value32 >> 8);
1345     Value->Data4[1] = (UINT8) Value32;
1346     if (mGlobals.SourceFile.FileBufferPtr[0] != '-') {
1347       goto Done;
1348     }
1349 
1350     mGlobals.SourceFile.FileBufferPtr++;
1351     //
1352     // Read the last 6 bytes of the GUID
1353     //
1354     //
1355     Len = GetHexChars (TempString, sizeof (TempString));
1356     if ((Len == 0) || (Len > 12)) {
1357       goto Done;
1358     }
1359     //
1360     // Insert leading 0's to make life easier
1361     //
1362     if (Len != 12) {
1363       From            = TempString + Len - 1;
1364       To              = TempString + 11;
1365       TempString[12]  = 0;
1366       while (From >= TempString) {
1367         *To = *From;
1368         To--;
1369         From--;
1370       }
1371 
1372       while (To >= TempString) {
1373         *To = '0';
1374         To--;
1375       }
1376     }
1377     //
1378     // Now parse each byte
1379     //
1380     TempString2[2] = 0;
1381     for (Index = 0; Index < 6; Index++) {
1382       //
1383       // Copy the two characters from the input string to something
1384       // we can parse.
1385       //
1386       TempString2[0]  = TempString[Index * 2];
1387       TempString2[1]  = TempString[Index * 2 + 1];
1388       sscanf (TempString2, "%x", &Value32);
1389       Value->Data4[Index + 2] = (UINT8) Value32;
1390     }
1391 
1392     Status = TRUE;
1393   } else {
1394     //
1395     // Unsupported GUID style
1396     //
1397     return FALSE;
1398   }
1399 
1400 Done:
1401   if (Status == FALSE) {
1402     SetFilePosition (&FPos);
1403   }
1404 
1405   return Status;
1406 }
1407 
1408 STATIC
1409 STATUS
GetFilePosition(FILE_POSITION * Fpos)1410 GetFilePosition (
1411   FILE_POSITION *Fpos
1412   )
1413 {
1414   Fpos->FileBufferPtr = mGlobals.SourceFile.FileBufferPtr;
1415   return STATUS_SUCCESS;
1416 }
1417 
1418 STATIC
1419 STATUS
SetFilePosition(FILE_POSITION * Fpos)1420 SetFilePosition (
1421   FILE_POSITION *Fpos
1422   )
1423 {
1424   //
1425   // Should check range of pointer
1426   //
1427   mGlobals.SourceFile.FileBufferPtr = Fpos->FileBufferPtr;
1428   return STATUS_SUCCESS;
1429 }
1430