| /****************************************************************************** |
| * |
| * Module Name: prutils - Preprocessor utilities |
| * |
| *****************************************************************************/ |
| |
| /* |
| * Copyright (C) 2000 - 2023, Intel Corp. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions, and the following disclaimer, |
| * without modification. |
| * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
| * substantially similar to the "NO WARRANTY" disclaimer below |
| * ("Disclaimer") and any redistribution must be conditioned upon |
| * including a substantially similar Disclaimer requirement for further |
| * binary redistribution. |
| * 3. Neither the names of the above-listed copyright holders nor the names |
| * of any contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * Alternatively, this software may be distributed under the terms of the |
| * GNU General Public License ("GPL") version 2 as published by the Free |
| * Software Foundation. |
| * |
| * NO WARRANTY |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
| * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGES. |
| */ |
| |
| #include "aslcompiler.h" |
| |
| #define _COMPONENT ASL_PREPROCESSOR |
| ACPI_MODULE_NAME ("prutils") |
| |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: PrGetNextToken |
| * |
| * PARAMETERS: Buffer - Current line buffer |
| * MatchString - String with valid token delimiters |
| * Next - Set to next possible token in buffer |
| * |
| * RETURN: Next token (null-terminated). Modifies the input line. |
| * Remainder of line is stored in *Next. |
| * |
| * DESCRIPTION: Local implementation of strtok() with local storage for the |
| * next pointer. Not only thread-safe, but allows multiple |
| * parsing of substrings such as expressions. |
| * |
| *****************************************************************************/ |
| |
| char * |
| PrGetNextToken ( |
| char *Buffer, |
| char *MatchString, |
| char **Next) |
| { |
| char *TokenStart; |
| |
| |
| if (!Buffer) |
| { |
| /* Use Next if it is valid */ |
| |
| Buffer = *Next; |
| if (!(*Next)) |
| { |
| return (NULL); |
| } |
| } |
| |
| /* Skip any leading delimiters */ |
| |
| while (*Buffer) |
| { |
| if (strchr (MatchString, *Buffer)) |
| { |
| Buffer++; |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| /* Anything left on the line? */ |
| |
| if (!(*Buffer)) |
| { |
| *Next = NULL; |
| return (NULL); |
| } |
| |
| TokenStart = Buffer; |
| |
| /* Find the end of this token */ |
| |
| while (*Buffer) |
| { |
| if (strchr (MatchString, *Buffer)) |
| { |
| *Buffer = 0; |
| *Next = Buffer+1; |
| if (!**Next) |
| { |
| *Next = NULL; |
| } |
| |
| return (TokenStart); |
| } |
| |
| Buffer++; |
| } |
| |
| *Next = NULL; |
| return (TokenStart); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: PrError |
| * |
| * PARAMETERS: Level - Seriousness (Warning/error, etc.) |
| * MessageId - Index into global message buffer |
| * Column - Column in current line |
| * |
| * RETURN: None |
| * |
| * DESCRIPTION: Preprocessor error reporting. Front end to AslCommonError2 |
| * |
| ******************************************************************************/ |
| |
| void |
| PrError ( |
| UINT8 Level, |
| UINT16 MessageId, |
| UINT32 Column) |
| { |
| #if 0 |
| AcpiOsPrintf ("%s (%u) : %s", AslGbl_Files[ASL_FILE_INPUT].Filename, |
| AslGbl_CurrentLineNumber, AslGbl_CurrentLineBuffer); |
| #endif |
| |
| |
| if (Column > 120) |
| { |
| Column = 0; |
| } |
| |
| /* TBD: Need Logical line number? */ |
| |
| AslCommonError2 (Level, MessageId, |
| AslGbl_CurrentLineNumber, Column, |
| AslGbl_CurrentLineBuffer, |
| AslGbl_Files[ASL_FILE_INPUT].Filename, "Preprocessor"); |
| |
| AslGbl_PreprocessorError = TRUE; |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: PrReplaceResizeSubstring |
| * |
| * PARAMETERS: Args - Struct containing name, offset & usecount |
| * Diff1 - Difference in lengths when new < old |
| * Diff2 - Difference in lengths when new > old |
| * i - The curr. no. of iteration of replacement |
| * Token - Substring that replaces Args->Name |
| * |
| * RETURN: None |
| * |
| * DESCRIPTION: Advanced substring replacement in a string using resized buffer. |
| * |
| ******************************************************************************/ |
| |
| void |
| PrReplaceResizeSubstring( |
| PR_MACRO_ARG *Args, |
| UINT32 Diff1, |
| UINT32 Diff2, |
| UINT32 i, |
| char *Token) |
| { |
| UINT32 b, PrevOffset; |
| char *temp; |
| char macro_sep[64]; |
| |
| |
| AslGbl_MacroTokenReplaceBuffer = (char *) realloc (AslGbl_MacroTokenReplaceBuffer, |
| (2 * (strlen (AslGbl_MacroTokenBuffer)))); |
| |
| strcpy (macro_sep, "~,() {}!*/%+-<>=&^|\"\t\n"); |
| |
| /* |
| * When the replacement argument (during invocation) length |
| * < replaced parameter (in the macro function definition |
| * and its expansion) length |
| */ |
| if (Diff1 != 0) |
| { |
| /* |
| * We save the offset value to reset it after replacing each |
| * instance of each arg and setting the offset value to |
| * the start of the arg to be replaced since it changes |
| * with each iteration when arg length != token length |
| */ |
| PrevOffset = Args->Offset[i]; |
| temp = strstr (AslGbl_MacroTokenBuffer, Args->Name); |
| |
| ResetHere1: |
| temp = strstr (temp, Args->Name); |
| Args->Offset[i] = strlen (AslGbl_MacroTokenBuffer) - |
| strlen (temp); |
| if (Args->Offset[i] == 0) |
| { |
| goto JumpHere1; |
| } |
| if ((strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] - 1)])) && |
| (strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] + strlen (Args->Name))]))) |
| { |
| Args->Offset[i] += 0; |
| } |
| else |
| { |
| temp += strlen (Args->Name); |
| goto ResetHere1; |
| } |
| |
| /* |
| * For now, we simply set the extra char positions (generated |
| * due to longer name replaced by shorter name) to whitespace |
| * chars so it will be ignored during compilation |
| */ |
| JumpHere1: |
| b = strlen (Token) + Args->Offset[i]; |
| memset (&AslGbl_MacroTokenBuffer[b], ' ', Diff1); |
| |
| # if 0 |
| |
| /* Work in progress as of 03/08/2023 - experimental 'if' block |
| * to test code for removing extra whitespaces from the macro |
| * replacement when replacement arg < replaced param |
| */ |
| char Buff[8192]; |
| /* char* Replace; */ |
| /* Replace = Buff; */ |
| |
| for (j = 0; j < strlen (AslGbl_MacroTokenBuffer); j++) |
| { |
| Buff[j] = AslGbl_MacroTokenBuffer[j]; |
| } |
| Buff[strlen (AslGbl_MacroTokenBuffer)] = '\0'; |
| //fprintf (stderr, "Buff: %s\n", Buff); |
| |
| UINT32 len = strlen (Buff); |
| |
| for (j = 0; j < len; j++) |
| { |
| if (Buff[0] == ' ') |
| { |
| for (j = 0; j < (len - 1); j++) |
| { |
| Buff[j] = Buff[j + 1]; |
| } |
| Buff[j] = '\0'; |
| len--; |
| j = -1; |
| continue; |
| } |
| |
| if (Buff[j] == ' ' && Buff[j + 1] == ' ') |
| { |
| for (k = 0; k < (len - 1); k++) |
| { |
| Buff[j] = Buff[j + 1]; |
| } |
| Buff[j] = '\0'; |
| len--; |
| j--; |
| } |
| } |
| //fprintf(stderr, "Buff: %s\n", Buff); |
| |
| for (k = 0; k < strlen (Buff); k++) |
| { |
| AslGbl_MacroTokenBuffer[k] = Buff[k]; |
| } |
| #endif |
| |
| PrReplaceData ( |
| &AslGbl_MacroTokenBuffer[Args->Offset[i]], |
| strlen (Token), Token, strlen (Token)); |
| |
| temp = NULL; |
| Args->Offset[i] = PrevOffset; |
| } |
| |
| /* |
| * When the replacement argument (during invocation) length |
| * > replaced parameter (in the macro function definition |
| * and its expansion) length |
| */ |
| else if (Diff2 != 0) |
| { |
| /* Doing the same thing with offset value as for prev case */ |
| |
| PrevOffset = Args->Offset[i]; |
| temp = strstr (AslGbl_MacroTokenBuffer, Args->Name); |
| |
| ResetHere2: |
| temp = strstr (temp, Args->Name); |
| Args->Offset[i] = strlen (AslGbl_MacroTokenBuffer) - |
| strlen (temp); |
| if (Args->Offset[i] == 0) |
| { |
| goto JumpHere2; |
| } |
| if ((strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] - 1)])) && |
| (strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] + strlen (Args->Name))]))) |
| { |
| Args->Offset[i] += 0; |
| } |
| else |
| { |
| temp+= strlen (Args->Name); |
| goto ResetHere2; |
| } |
| |
| /* |
| * We will need to allocate some extra space in our buffer to |
| * accommodate the increase in the replacement string length |
| * over the shorter outgoing arg string and do the replacement |
| * at the correct offset value which is resetted every iteration |
| */ |
| JumpHere2: |
| strncpy (AslGbl_MacroTokenReplaceBuffer, AslGbl_MacroTokenBuffer, Args->Offset[i]); |
| strcat (AslGbl_MacroTokenReplaceBuffer, Token); |
| strcat (AslGbl_MacroTokenReplaceBuffer, (AslGbl_MacroTokenBuffer + |
| (Args->Offset[i] + strlen (Args->Name)))); |
| |
| strcpy (AslGbl_MacroTokenBuffer, AslGbl_MacroTokenReplaceBuffer); |
| |
| temp = NULL; |
| Args->Offset[i] = PrevOffset; |
| } |
| |
| /* |
| * When the replacement argument (during invocation) length = |
| * replaced parameter (in the macro function definition and |
| * its expansion) length |
| */ |
| else |
| { |
| |
| /* |
| * We still need to reset the offset for each iteration even when |
| * arg and param lengths are same since any macro func invocation |
| * could use various cases for each separate arg-param pair |
| */ |
| PrevOffset = Args->Offset[i]; |
| temp = strstr (AslGbl_MacroTokenBuffer, Args->Name); |
| |
| ResetHere3: |
| temp = strstr (temp, Args->Name); |
| Args->Offset[i] = strlen (AslGbl_MacroTokenBuffer) - |
| strlen (temp); |
| if (Args->Offset[i] == 0) |
| { |
| goto JumpHere3; |
| } |
| if ((strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] - 1)])) && |
| (strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] + strlen (Args->Name))]))) |
| { |
| Args->Offset[i] += 0; |
| } |
| else |
| { |
| temp += strlen (Args->Name); |
| goto ResetHere3; |
| } |
| |
| JumpHere3: |
| PrReplaceData ( |
| &AslGbl_MacroTokenBuffer[Args->Offset[i]], |
| strlen (Args->Name), Token, strlen (Token)); |
| temp = NULL; |
| Args->Offset[i] = PrevOffset; |
| } |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: PrReplaceData |
| * |
| * PARAMETERS: Buffer - Original(target) buffer pointer |
| * LengthToRemove - Length to be removed from target buffer |
| * BufferToAdd - Data to be inserted into target buffer |
| * LengthToAdd - Length of BufferToAdd |
| * |
| * RETURN: Pointer to where the buffer is replaced with data |
| * |
| * DESCRIPTION: Generic buffer data replacement. |
| * |
| ******************************************************************************/ |
| |
| char * |
| PrReplaceData ( |
| char *Buffer, |
| UINT32 LengthToRemove, |
| char *BufferToAdd, |
| UINT32 LengthToAdd) |
| { |
| UINT32 BufferLength; |
| |
| |
| /* Buffer is a string, so the length must include the terminating zero */ |
| |
| BufferLength = strlen (Buffer) + 1; |
| |
| if (LengthToRemove != LengthToAdd) |
| { |
| /* |
| * Move some of the existing data |
| * 1) If adding more bytes than removing, make room for the new data |
| * 2) if removing more bytes than adding, delete the extra space |
| */ |
| if (LengthToRemove > 0) |
| { |
| memmove ((Buffer + LengthToAdd), (Buffer + LengthToRemove), |
| (BufferLength - LengthToRemove)); |
| } |
| } |
| |
| |
| /* Now we can move in the new data */ |
| |
| if (LengthToAdd > 0) |
| { |
| memmove (Buffer, BufferToAdd, LengthToAdd); |
| } |
| return (Buffer + LengthToAdd); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: PrOpenIncludeFile |
| * |
| * PARAMETERS: Filename - Filename or pathname for include file |
| * |
| * RETURN: None. |
| * |
| * DESCRIPTION: Open an include file and push it on the input file stack. |
| * |
| ******************************************************************************/ |
| |
| FILE * |
| PrOpenIncludeFile ( |
| char *Filename, |
| char *OpenMode, |
| char **FullPathname) |
| { |
| FILE *IncludeFile; |
| ASL_INCLUDE_DIR *NextDir; |
| |
| |
| /* Start the actual include file on the next line */ |
| |
| AslGbl_CurrentLineOffset++; |
| |
| /* Attempt to open the include file */ |
| /* If the file specifies an absolute path, just open it */ |
| |
| if ((Filename[0] == '/') || |
| (Filename[0] == '\\') || |
| (Filename[1] == ':')) |
| { |
| IncludeFile = PrOpenIncludeWithPrefix ( |
| "", Filename, OpenMode, FullPathname); |
| if (!IncludeFile) |
| { |
| goto ErrorExit; |
| } |
| return (IncludeFile); |
| } |
| |
| /* |
| * The include filename is not an absolute path. |
| * |
| * First, search for the file within the "local" directory -- meaning |
| * the same directory that contains the source file. |
| * |
| * Construct the file pathname from the global directory name. |
| */ |
| IncludeFile = PrOpenIncludeWithPrefix ( |
| AslGbl_DirectoryPath, Filename, OpenMode, FullPathname); |
| if (IncludeFile) |
| { |
| return (IncludeFile); |
| } |
| |
| /* |
| * Second, search for the file within the (possibly multiple) |
| * directories specified by the -I option on the command line. |
| */ |
| NextDir = AslGbl_IncludeDirList; |
| while (NextDir) |
| { |
| IncludeFile = PrOpenIncludeWithPrefix ( |
| NextDir->Dir, Filename, OpenMode, FullPathname); |
| if (IncludeFile) |
| { |
| return (IncludeFile); |
| } |
| |
| NextDir = NextDir->Next; |
| } |
| |
| /* We could not open the include file after trying very hard */ |
| |
| ErrorExit: |
| sprintf (AslGbl_MainTokenBuffer, "%s, %s", Filename, strerror (errno)); |
| PrError (ASL_ERROR, ASL_MSG_INCLUDE_FILE_OPEN, 0); |
| return (NULL); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: FlOpenIncludeWithPrefix |
| * |
| * PARAMETERS: PrefixDir - Prefix directory pathname. Can be a zero |
| * length string. |
| * Filename - The include filename from the source ASL. |
| * |
| * RETURN: Valid file descriptor if successful. Null otherwise. |
| * |
| * DESCRIPTION: Open an include file and push it on the input file stack. |
| * |
| ******************************************************************************/ |
| |
| FILE * |
| PrOpenIncludeWithPrefix ( |
| char *PrefixDir, |
| char *Filename, |
| char *OpenMode, |
| char **FullPathname) |
| { |
| FILE *IncludeFile; |
| char *Pathname; |
| |
| |
| /* Build the full pathname to the file */ |
| |
| Pathname = FlMergePathnames (PrefixDir, Filename); |
| |
| DbgPrint (ASL_PARSE_OUTPUT, PR_PREFIX_ID |
| "Include: Opening file - \"%s\"\n", |
| AslGbl_CurrentLineNumber, Pathname); |
| |
| /* Attempt to open the file, push if successful */ |
| |
| IncludeFile = fopen (Pathname, OpenMode); |
| if (!IncludeFile) |
| { |
| return (NULL); |
| } |
| |
| /* Push the include file on the open input file stack */ |
| |
| PrPushInputFileStack (IncludeFile, Pathname); |
| *FullPathname = Pathname; |
| return (IncludeFile); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AslPushInputFileStack |
| * |
| * PARAMETERS: InputFile - Open file pointer |
| * Filename - Name of the file |
| * |
| * RETURN: None |
| * |
| * DESCRIPTION: Push the InputFile onto the file stack, and point the parser |
| * to this file. Called when an include file is successfully |
| * opened. |
| * |
| ******************************************************************************/ |
| |
| void |
| PrPushInputFileStack ( |
| FILE *InputFile, |
| char *Filename) |
| { |
| PR_FILE_NODE *Fnode; |
| |
| |
| AslGbl_HasIncludeFiles = TRUE; |
| |
| /* Save the current state in an Fnode */ |
| |
| Fnode = UtLocalCalloc (sizeof (PR_FILE_NODE)); |
| |
| Fnode->File = AslGbl_Files[ASL_FILE_INPUT].Handle; |
| Fnode->Next = AslGbl_InputFileList; |
| Fnode->Filename = AslGbl_Files[ASL_FILE_INPUT].Filename; |
| Fnode->CurrentLineNumber = AslGbl_CurrentLineNumber; |
| |
| /* Push it on the stack */ |
| |
| AslGbl_InputFileList = Fnode; |
| |
| DbgPrint (ASL_PARSE_OUTPUT, PR_PREFIX_ID |
| "Push InputFile Stack: handle %p\n\n", |
| AslGbl_CurrentLineNumber, InputFile); |
| |
| /* Reset the global line count and filename */ |
| |
| AslGbl_Files[ASL_FILE_INPUT].Filename = |
| UtLocalCacheCalloc (strlen (Filename) + 1); |
| strcpy (AslGbl_Files[ASL_FILE_INPUT].Filename, Filename); |
| |
| AslGbl_Files[ASL_FILE_INPUT].Handle = InputFile; |
| AslGbl_CurrentLineNumber = 1; |
| |
| /* Emit a new #line directive for the include file */ |
| |
| FlPrintFile (ASL_FILE_PREPROCESSOR, "#line %u \"%s\"\n", 1, Filename); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AslPopInputFileStack |
| * |
| * PARAMETERS: None |
| * |
| * RETURN: 0 if a node was popped, -1 otherwise |
| * |
| * DESCRIPTION: Pop the top of the input file stack and point the parser to |
| * the saved parse buffer contained in the fnode. Also, set the |
| * global line counters to the saved values. This function is |
| * called when an include file reaches EOF. |
| * |
| ******************************************************************************/ |
| |
| BOOLEAN |
| PrPopInputFileStack ( |
| void) |
| { |
| PR_FILE_NODE *Fnode; |
| |
| |
| Fnode = AslGbl_InputFileList; |
| DbgPrint (ASL_PARSE_OUTPUT, "\n" PR_PREFIX_ID |
| "Pop InputFile Stack, Fnode %p\n\n", |
| AslGbl_CurrentLineNumber, Fnode); |
| |
| if (!Fnode) |
| { |
| return (FALSE); |
| } |
| |
| /* Close the current include file */ |
| |
| fclose (AslGbl_Files[ASL_FILE_INPUT].Handle); |
| |
| /* Update the top-of-stack */ |
| |
| AslGbl_InputFileList = Fnode->Next; |
| |
| /* Reset global line counter and filename */ |
| |
| AslGbl_Files[ASL_FILE_INPUT].Filename = Fnode->Filename; |
| AslGbl_Files[ASL_FILE_INPUT].Handle = Fnode->File; |
| AslGbl_CurrentLineNumber = Fnode->CurrentLineNumber; |
| |
| /* Emit a new #line directive after the include file */ |
| |
| FlPrintFile (ASL_FILE_PREPROCESSOR, "#line %u \"%s\"\n", |
| AslGbl_CurrentLineNumber, Fnode->Filename); |
| |
| /* All done with this node */ |
| |
| ACPI_FREE (Fnode); |
| return (TRUE); |
| } |