3 Copyright (c) 2004, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Generic but simple file parsing routines.
27 #include <Common/UefiBaseTypes.h>
29 #include "EfiUtilityMsgs.h"
30 #include "SimpleFileParsing.h"
33 #define MAX_NEST_DEPTH 20 // just in case we get in an endless loop.
34 #define MAX_STRING_IDENTIFIER_NAME 100 // number of wchars
35 #define MAX_LINE_LEN 400
37 #define T_CHAR_SPACE ' '
39 #define T_CHAR_CR '\r'
40 #define T_CHAR_TAB '\t'
41 #define T_CHAR_LF '\n'
42 #define T_CHAR_SLASH '/'
43 #define T_CHAR_BACKSLASH '\\'
44 #define T_CHAR_DOUBLE_QUOTE '"'
45 #define T_CHAR_LC_X 'x'
49 // We keep a linked list of these for the source files we process
51 typedef struct _SOURCE_FILE
{
54 T_CHAR
*FileBufferPtr
;
56 INT8 FileName
[MAX_PATH
];
60 struct _SOURCE_FILE
*Previous
;
61 struct _SOURCE_FILE
*Next
;
62 T_CHAR ControlCharacter
;
66 // Here's all our module globals.
69 SOURCE_FILE SourceFile
;
97 SOURCE_FILE
*SourceFile
103 SOURCE_FILE
*SourceFile
,
105 BOOLEAN StopAfterNewline
111 SOURCE_FILE
*SourceFile
117 SOURCE_FILE
*SourceFile
123 SOURCE_FILE
*SourceFile
129 SOURCE_FILE
*SourceFile
136 // SOURCE_FILE *SourceFile,
150 SOURCE_FILE
*SourceFile
,
151 SOURCE_FILE
*ParentSourceFile
157 SOURCE_FILE
*SourceFile
164 OUT INT8
*FoundFileName
,
165 IN UINT32 FoundFileNameLen
171 SOURCE_FILE
*SourceFile
179 memset ((void *) &mGlobals
, 0, sizeof (mGlobals
));
180 return STATUS_SUCCESS
;
188 return mGlobals
.SourceFile
.LineNum
;
194 Return the line number of the file we're parsing. Used
195 for error reporting purposes.
201 The line number, or 0 if no file is being processed
211 Return the name of the file we're parsing. Used
212 for error reporting purposes.
218 A pointer to the file name. Null if no file is being
223 if (mGlobals
.SourceFile
.FileName
[0]) {
224 return mGlobals
.SourceFile
.FileName
;
237 Open a file for parsing.
240 FileName - name of the file to parse
248 t_strcpy (mGlobals
.SourceFile
.FileName
, FileName
);
249 Status
= ProcessIncludeFile (&mGlobals
.SourceFile
, NULL
);
260 Check to see if the specified token is found at
261 the current position in the input file.
264 Str - the token to look for
267 TRUE - the token is next
268 FALSE - the token is not next
271 We do a simple string comparison on this function. It is
272 the responsibility of the caller to ensure that the token
273 is not a subset of some other token.
275 The file pointer is advanced past the token in the input file.
280 SkipWhiteSpace (&mGlobals
.SourceFile
);
282 if ((Len
= t_strcmp (mGlobals
.SourceFile
.FileBufferPtr
, Str
)) > 0) {
283 mGlobals
.SourceFile
.FileBufferPtr
+= Len
;
298 SkipWhiteSpace (&mGlobals
.SourceFile
);
300 while (!EndOfFile (&mGlobals
.SourceFile
) && (Index
< Len
)) {
301 if (IsWhiteSpace (&mGlobals
.SourceFile
)) {
309 Str
[Index
] = mGlobals
.SourceFile
.FileBufferPtr
[0];
310 mGlobals
.SourceFile
.FileBufferPtr
++;
325 Len
= t_strlen (Str
);
326 SavePos
= mGlobals
.SourceFile
.FileBufferPtr
;
327 SkipWhiteSpace (&mGlobals
.SourceFile
);
328 while (!EndOfFile (&mGlobals
.SourceFile
)) {
329 if (t_strncmp (Str
, mGlobals
.SourceFile
.FileBufferPtr
, Len
) == 0) {
330 mGlobals
.SourceFile
.FileBufferPtr
+= Len
;
334 mGlobals
.SourceFile
.FileBufferPtr
++;
335 SkipWhiteSpace (&mGlobals
.SourceFile
);
338 mGlobals
.SourceFile
.FileBufferPtr
= SavePos
;
349 Check the token at the current file position for a numeric value.
350 May be either decimal or hex.
353 Value - pointer where to store the value
356 FALSE - current token is not a number
357 TRUE - current token is a number
364 SkipWhiteSpace (&mGlobals
.SourceFile
);
365 if (EndOfFile (&mGlobals
.SourceFile
)) {
369 if (isdigit (mGlobals
.SourceFile
.FileBufferPtr
[0])) {
371 // Check for hex value
373 if ((mGlobals
.SourceFile
.FileBufferPtr
[0] == T_CHAR_0
) && (mGlobals
.SourceFile
.FileBufferPtr
[1] == T_CHAR_LC_X
)) {
374 if (!isxdigit (mGlobals
.SourceFile
.FileBufferPtr
[2])) {
378 mGlobals
.SourceFile
.FileBufferPtr
+= 2;
379 sscanf (mGlobals
.SourceFile
.FileBufferPtr
, "%x", Value
);
380 while (isxdigit (mGlobals
.SourceFile
.FileBufferPtr
[0])) {
381 mGlobals
.SourceFile
.FileBufferPtr
++;
386 *Value
= atoi (mGlobals
.SourceFile
.FileBufferPtr
);
387 while (isdigit (mGlobals
.SourceFile
.FileBufferPtr
[0])) {
388 mGlobals
.SourceFile
.FileBufferPtr
++;
405 Close the file being parsed.
411 STATUS_SUCCESS - the file was closed
412 STATUS_ERROR - no file is currently open
416 if (mGlobals
.SourceFile
.FileBuffer
!= NULL
) {
417 free (mGlobals
.SourceFile
.FileBuffer
);
418 memset (&mGlobals
.SourceFile
, 0, sizeof (mGlobals
.SourceFile
));
419 return STATUS_SUCCESS
;
428 SOURCE_FILE
*SourceFile
,
429 SOURCE_FILE
*ParentSourceFile
435 Given a source file, open the file and parse it
439 SourceFile - name of file to parse
440 ParentSourceFile - for error reporting purposes, the file that #included SourceFile.
448 static UINT32 NestDepth
= 0;
449 INT8 FoundFileName
[MAX_PATH
];
452 Status
= STATUS_SUCCESS
;
455 // Print the file being processed. Indent so you can tell the include nesting
458 if (mGlobals
.Verbose
) {
459 fprintf (stdout
, "%*cProcessing file '%s'\n", NestDepth
* 2, ' ', SourceFile
->FileName
);
463 // Make sure we didn't exceed our maximum nesting depth
465 if (NestDepth
> MAX_NEST_DEPTH
) {
466 Error (NULL
, 0, 0, SourceFile
->FileName
, "max nesting depth (%d) exceeded", NestDepth
);
467 Status
= STATUS_ERROR
;
471 // Try to open the file locally, and if that fails try along our include paths.
473 strcpy (FoundFileName
, SourceFile
->FileName
);
474 if ((SourceFile
->Fptr
= fopen (FoundFileName
, "r")) == NULL
) {
476 // Try to find it among the paths if it has a parent (that is, it is included
479 Error (NULL
, 0, 0, SourceFile
->FileName
, "file not found");
483 // Process the file found
485 ProcessFile (SourceFile
);
488 // Close open files and return status
490 if (SourceFile
->Fptr
!= NULL
) {
491 fclose (SourceFile
->Fptr
);
492 SourceFile
->Fptr
= NULL
;
501 SOURCE_FILE
*SourceFile
505 // Get the file size, and then read the entire thing into memory.
506 // Allocate space for a terminator character.
508 fseek (SourceFile
->Fptr
, 0, SEEK_END
);
509 SourceFile
->FileSize
= ftell (SourceFile
->Fptr
);
510 fseek (SourceFile
->Fptr
, 0, SEEK_SET
);
511 SourceFile
->FileBuffer
= (T_CHAR
*) malloc (SourceFile
->FileSize
+ sizeof (T_CHAR
));
512 if (SourceFile
->FileBuffer
== NULL
) {
513 Error (NULL
, 0, 0, "memory allocation failure", NULL
);
517 fread ((VOID
*) SourceFile
->FileBuffer
, SourceFile
->FileSize
, 1, SourceFile
->Fptr
);
518 SourceFile
->FileBuffer
[(SourceFile
->FileSize
/ sizeof (T_CHAR
))] = T_CHAR_NULL
;
520 // Pre-process the file to replace comments with spaces
522 PreprocessFile (SourceFile
);
523 SourceFile
->LineNum
= 1;
524 return STATUS_SUCCESS
;
530 SOURCE_FILE
*SourceFile
535 Preprocess a file to replace all carriage returns with NULLs so
536 we can print lines from the file to the screen.
539 SourceFile - structure that we use to keep track of an input file.
548 RewindFile (SourceFile
);
550 while (!EndOfFile (SourceFile
)) {
552 // If a line-feed, then no longer in a comment
554 if (SourceFile
->FileBufferPtr
[0] == T_CHAR_LF
) {
555 SourceFile
->FileBufferPtr
++;
556 SourceFile
->LineNum
++;
558 } else if (SourceFile
->FileBufferPtr
[0] == T_CHAR_CR
) {
560 // Replace all carriage returns with a NULL so we can print stuff
562 SourceFile
->FileBufferPtr
[0] = 0;
563 SourceFile
->FileBufferPtr
++;
564 } else if (InComment
) {
565 SourceFile
->FileBufferPtr
[0] = T_CHAR_SPACE
;
566 SourceFile
->FileBufferPtr
++;
567 } else if ((SourceFile
->FileBufferPtr
[0] == T_CHAR_SLASH
) && (SourceFile
->FileBufferPtr
[1] == T_CHAR_SLASH
)) {
568 SourceFile
->FileBufferPtr
+= 2;
571 SourceFile
->FileBufferPtr
++;
575 // Could check for end-of-file and still in a comment, but
576 // should not be necessary. So just restore the file pointers.
578 RewindFile (SourceFile
);
585 SOURCE_FILE
*SourceFile
,
593 BOOLEAN PreviousBackslash
;
595 if (SourceFile
->FileBufferPtr
[0] != T_CHAR_DOUBLE_QUOTE
) {
597 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted string", "%S", SourceFile
->FileBufferPtr
);
604 SourceFile
->FileBufferPtr
++;
605 Start
= Ptr
= SourceFile
->FileBufferPtr
;
606 PreviousBackslash
= FALSE
;
607 while (!EndOfFile (SourceFile
)) {
608 if ((SourceFile
->FileBufferPtr
[0] == T_CHAR_DOUBLE_QUOTE
) && (!PreviousBackslash
)) {
610 } else if (SourceFile
->FileBufferPtr
[0] == T_CHAR_CR
) {
611 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "carriage return found in quoted string", "%S", Start
);
612 PreviousBackslash
= FALSE
;
613 } else if (SourceFile
->FileBufferPtr
[0] == T_CHAR_BACKSLASH
) {
614 PreviousBackslash
= TRUE
;
616 PreviousBackslash
= FALSE
;
619 SourceFile
->FileBufferPtr
++;
623 if (SourceFile
->FileBufferPtr
[0] != T_CHAR_DOUBLE_QUOTE
) {
624 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "missing closing quote on string", "%S", Start
);
626 SourceFile
->FileBufferPtr
++;
629 // Now allocate memory for the string and save it off
631 String
= (T_CHAR
*) malloc ((Len
+ 1) * sizeof (T_CHAR
));
632 if (String
== NULL
) {
633 Error (NULL
, 0, 0, "memory allocation failed", NULL
);
637 // Copy the string from the file buffer to the local copy.
638 // We do no reformatting of it whatsoever at this point.
655 SOURCE_FILE
*SourceFile
659 // The file buffer pointer will typically get updated before the End-of-file flag in the
660 // source file structure, so check it first.
662 if (SourceFile
->FileBufferPtr
>= SourceFile
->FileBuffer
+ SourceFile
->FileSize
/ sizeof (T_CHAR
)) {
663 SourceFile
->EndOfFile
= TRUE
;
667 if (SourceFile
->EndOfFile
) {
677 ProcessTokenInclude (
678 SOURCE_FILE
*SourceFile
681 INT8 IncludeFileName
[MAX_PATH
];
684 BOOLEAN ReportedError
;
685 SOURCE_FILE IncludedSourceFile
;
687 ReportedError
= FALSE
;
688 if (SkipWhiteSpace (SourceFile
) == 0) {
689 Warning (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected whitespace following #include keyword", NULL
);
692 // Should be quoted file name
694 if (SourceFile
->FileBufferPtr
[0] != T_CHAR_DOUBLE_QUOTE
) {
695 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "expected quoted include file name", NULL
);
699 SourceFile
->FileBufferPtr
++;
701 // Copy the filename as ascii to our local string
703 To
= IncludeFileName
;
705 while (!EndOfFile (SourceFile
)) {
706 if ((SourceFile
->FileBufferPtr
[0] == T_CHAR_CR
) || (SourceFile
->FileBufferPtr
[0] == T_CHAR_LF
)) {
707 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "end-of-line found in quoted include file name", NULL
);
711 if (SourceFile
->FileBufferPtr
[0] == T_CHAR_DOUBLE_QUOTE
) {
712 SourceFile
->FileBufferPtr
++;
716 // If too long, then report the error once and process until the closing quote
719 if (!ReportedError
&& (Len
>= sizeof (IncludeFileName
))) {
720 Error (SourceFile
->FileName
, SourceFile
->LineNum
, 0, "length of include file name exceeds limit", NULL
);
721 ReportedError
= TRUE
;
724 if (!ReportedError
) {
726 // *To = UNICODE_TO_ASCII(SourceFile->FileBufferPtr[0]);
728 *To
= (T_CHAR
) SourceFile
->FileBufferPtr
[0];
732 SourceFile
->FileBufferPtr
++;
735 if (!ReportedError
) {
737 memset ((char *) &IncludedSourceFile
, 0, sizeof (SOURCE_FILE
));
738 strcpy (IncludedSourceFile
.FileName
, IncludeFileName
);
740 // IncludedSourceFile.ControlCharacter = DEFAULT_CONTROL_CHARACTER;
742 ProcessIncludeFile (&IncludedSourceFile
, SourceFile
);
744 // printf ("including file '%s'\n", IncludeFileName);
751 // Error recovery -- skip to next #
753 SourceFile
->SkipToHash
= TRUE
;
759 SOURCE_FILE
*SourceFile
762 switch (*SourceFile
->FileBufferPtr
) {
777 SOURCE_FILE
*SourceFile
783 while (!EndOfFile (SourceFile
)) {
785 switch (*SourceFile
->FileBufferPtr
) {
790 SourceFile
->FileBufferPtr
++;
794 SourceFile
->FileBufferPtr
++;
795 SourceFile
->LineNum
++;
796 if (mGlobals
.Verbose
) {
797 printf ("%d: %S\n", SourceFile
->LineNum
, SourceFile
->FileBufferPtr
);
806 // Some tokens require trailing whitespace. If we're at the end of the
807 // file, then we count that as well.
809 if ((Count
== 0) && (EndOfFile (SourceFile
))) {
826 while (*Str
== *Buffer
) {
864 if (*Str1
!= *Str2
) {
898 IsValidIdentifierChar (
904 // If it's the first character of an identifier, then
905 // it must be one of [A-Za-z_].
908 if (isalpha (Char
) || (Char
== '_')) {
913 // If it's not the first character, then it can
914 // be one of [A-Za-z_0-9]
916 if (isalnum (Char
) || (Char
== '_')) {
927 SOURCE_FILE
*SourceFile
930 SourceFile
->LineNum
= 1;
931 SourceFile
->FileBufferPtr
= SourceFile
->FileBuffer
;
932 SourceFile
->EndOfFile
= 0;
939 SOURCE_FILE
*SourceFile
,
941 BOOLEAN StopAfterNewline
944 while (!EndOfFile (SourceFile
)) {
946 // Check for the character of interest
948 if (SourceFile
->FileBufferPtr
[0] == TChar
) {
951 if (SourceFile
->FileBufferPtr
[0] == T_CHAR_LF
) {
952 SourceFile
->LineNum
++;
953 if (StopAfterNewline
) {
954 SourceFile
->FileBufferPtr
++;
955 if (SourceFile
->FileBufferPtr
[0] == 0) {
956 SourceFile
->FileBufferPtr
++;
963 SourceFile
->FileBufferPtr
++;