1 /****************************************************************************** 2 * 3 * Module Name: prutils - Preprocessor utilities 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2023, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 #include "aslcompiler.h" 45 46 #define _COMPONENT ASL_PREPROCESSOR 47 ACPI_MODULE_NAME ("prutils") 48 49 50 /****************************************************************************** 51 * 52 * FUNCTION: PrGetNextToken 53 * 54 * PARAMETERS: Buffer - Current line buffer 55 * MatchString - String with valid token delimiters 56 * Next - Set to next possible token in buffer 57 * 58 * RETURN: Next token (null-terminated). Modifies the input line. 59 * Remainder of line is stored in *Next. 60 * 61 * DESCRIPTION: Local implementation of strtok() with local storage for the 62 * next pointer. Not only thread-safe, but allows multiple 63 * parsing of substrings such as expressions. 64 * 65 *****************************************************************************/ 66 67 char * 68 PrGetNextToken ( 69 char *Buffer, 70 char *MatchString, 71 char **Next) 72 { 73 char *TokenStart; 74 75 76 if (!Buffer) 77 { 78 /* Use Next if it is valid */ 79 80 Buffer = *Next; 81 if (!(*Next)) 82 { 83 return (NULL); 84 } 85 } 86 87 /* Skip any leading delimiters */ 88 89 while (*Buffer) 90 { 91 if (strchr (MatchString, *Buffer)) 92 { 93 Buffer++; 94 } 95 else 96 { 97 break; 98 } 99 } 100 101 /* Anything left on the line? */ 102 103 if (!(*Buffer)) 104 { 105 *Next = NULL; 106 return (NULL); 107 } 108 109 TokenStart = Buffer; 110 111 /* Find the end of this token */ 112 113 while (*Buffer) 114 { 115 if (strchr (MatchString, *Buffer)) 116 { 117 *Buffer = 0; 118 *Next = Buffer+1; 119 if (!**Next) 120 { 121 *Next = NULL; 122 } 123 124 return (TokenStart); 125 } 126 127 Buffer++; 128 } 129 130 *Next = NULL; 131 return (TokenStart); 132 } 133 134 135 /******************************************************************************* 136 * 137 * FUNCTION: PrError 138 * 139 * PARAMETERS: Level - Seriousness (Warning/error, etc.) 140 * MessageId - Index into global message buffer 141 * Column - Column in current line 142 * 143 * RETURN: None 144 * 145 * DESCRIPTION: Preprocessor error reporting. Front end to AslCommonError2 146 * 147 ******************************************************************************/ 148 149 void 150 PrError ( 151 UINT8 Level, 152 UINT16 MessageId, 153 UINT32 Column) 154 { 155 #if 0 156 AcpiOsPrintf ("%s (%u) : %s", AslGbl_Files[ASL_FILE_INPUT].Filename, 157 AslGbl_CurrentLineNumber, AslGbl_CurrentLineBuffer); 158 #endif 159 160 161 if (Column > 120) 162 { 163 Column = 0; 164 } 165 166 /* TBD: Need Logical line number? */ 167 168 AslCommonError2 (Level, MessageId, 169 AslGbl_CurrentLineNumber, Column, 170 AslGbl_CurrentLineBuffer, 171 AslGbl_Files[ASL_FILE_INPUT].Filename, "Preprocessor"); 172 173 AslGbl_PreprocessorError = TRUE; 174 } 175 176 177 /******************************************************************************* 178 * 179 * FUNCTION: PrReplaceResizeSubstring 180 * 181 * PARAMETERS: Args - Struct containing name, offset & usecount 182 * Diff1 - Difference in lengths when new < old 183 * Diff2 - Difference in lengths when new > old 184 * i - The curr. no. of iteration of replacement 185 * Token - Substring that replaces Args->Name 186 * 187 * RETURN: None 188 * 189 * DESCRIPTION: Advanced substring replacement in a string using resized buffer. 190 * 191 ******************************************************************************/ 192 193 void 194 PrReplaceResizeSubstring( 195 PR_MACRO_ARG *Args, 196 UINT32 Diff1, 197 UINT32 Diff2, 198 UINT32 i, 199 char *Token) 200 { 201 UINT32 b, PrevOffset; 202 char *temp; 203 char macro_sep[64]; 204 205 206 AslGbl_MacroTokenReplaceBuffer = (char *) realloc (AslGbl_MacroTokenReplaceBuffer, 207 (2 * (strlen (AslGbl_MacroTokenBuffer)))); 208 209 strcpy (macro_sep, "~,() {}!*/%+-<>=&^|\"\t\n"); 210 211 /* 212 * When the replacement argument (during invocation) length 213 * < replaced parameter (in the macro function definition 214 * and its expansion) length 215 */ 216 if (Diff1 != 0) 217 { 218 /* 219 * We save the offset value to reset it after replacing each 220 * instance of each arg and setting the offset value to 221 * the start of the arg to be replaced since it changes 222 * with each iteration when arg length != token length 223 */ 224 PrevOffset = Args->Offset[i]; 225 temp = strstr (AslGbl_MacroTokenBuffer, Args->Name); 226 227 ResetHere1: 228 temp = strstr (temp, Args->Name); 229 Args->Offset[i] = strlen (AslGbl_MacroTokenBuffer) - 230 strlen (temp); 231 if (Args->Offset[i] == 0) 232 { 233 goto JumpHere1; 234 } 235 if ((strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] - 1)])) && 236 (strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] + strlen (Args->Name))]))) 237 { 238 Args->Offset[i] += 0; 239 } 240 else 241 { 242 temp += strlen (Args->Name); 243 goto ResetHere1; 244 } 245 246 /* 247 * For now, we simply set the extra char positions (generated 248 * due to longer name replaced by shorter name) to whitespace 249 * chars so it will be ignored during compilation 250 */ 251 JumpHere1: 252 b = strlen (Token) + Args->Offset[i]; 253 memset (&AslGbl_MacroTokenBuffer[b], ' ', Diff1); 254 255 # if 0 256 257 /* Work in progress as of 03/08/2023 - experimental 'if' block 258 * to test code for removing extra whitespaces from the macro 259 * replacement when replacement arg < replaced param 260 */ 261 char Buff[8192]; 262 /* char* Replace; */ 263 /* Replace = Buff; */ 264 265 for (j = 0; j < strlen (AslGbl_MacroTokenBuffer); j++) 266 { 267 Buff[j] = AslGbl_MacroTokenBuffer[j]; 268 } 269 Buff[strlen (AslGbl_MacroTokenBuffer)] = '\0'; 270 //fprintf (stderr, "Buff: %s\n", Buff); 271 272 UINT32 len = strlen (Buff); 273 274 for (j = 0; j < len; j++) 275 { 276 if (Buff[0] == ' ') 277 { 278 for (j = 0; j < (len - 1); j++) 279 { 280 Buff[j] = Buff[j + 1]; 281 } 282 Buff[j] = '\0'; 283 len--; 284 j = -1; 285 continue; 286 } 287 288 if (Buff[j] == ' ' && Buff[j + 1] == ' ') 289 { 290 for (k = 0; k < (len - 1); k++) 291 { 292 Buff[j] = Buff[j + 1]; 293 } 294 Buff[j] = '\0'; 295 len--; 296 j--; 297 } 298 } 299 //fprintf(stderr, "Buff: %s\n", Buff); 300 301 for (k = 0; k < strlen (Buff); k++) 302 { 303 AslGbl_MacroTokenBuffer[k] = Buff[k]; 304 } 305 #endif 306 307 PrReplaceData ( 308 &AslGbl_MacroTokenBuffer[Args->Offset[i]], 309 strlen (Token), Token, strlen (Token)); 310 311 temp = NULL; 312 Args->Offset[i] = PrevOffset; 313 } 314 315 /* 316 * When the replacement argument (during invocation) length 317 * > replaced parameter (in the macro function definition 318 * and its expansion) length 319 */ 320 else if (Diff2 != 0) 321 { 322 /* Doing the same thing with offset value as for prev case */ 323 324 PrevOffset = Args->Offset[i]; 325 temp = strstr (AslGbl_MacroTokenBuffer, Args->Name); 326 327 ResetHere2: 328 temp = strstr (temp, Args->Name); 329 Args->Offset[i] = strlen (AslGbl_MacroTokenBuffer) - 330 strlen (temp); 331 if (Args->Offset[i] == 0) 332 { 333 goto JumpHere2; 334 } 335 if ((strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] - 1)])) && 336 (strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] + strlen (Args->Name))]))) 337 { 338 Args->Offset[i] += 0; 339 } 340 else 341 { 342 temp+= strlen (Args->Name); 343 goto ResetHere2; 344 } 345 346 /* 347 * We will need to allocate some extra space in our buffer to 348 * accommodate the increase in the replacement string length 349 * over the shorter outgoing arg string and do the replacement 350 * at the correct offset value which is resetted every iteration 351 */ 352 JumpHere2: 353 strncpy (AslGbl_MacroTokenReplaceBuffer, AslGbl_MacroTokenBuffer, Args->Offset[i]); 354 strcat (AslGbl_MacroTokenReplaceBuffer, Token); 355 strcat (AslGbl_MacroTokenReplaceBuffer, (AslGbl_MacroTokenBuffer + 356 (Args->Offset[i] + strlen (Args->Name)))); 357 358 strcpy (AslGbl_MacroTokenBuffer, AslGbl_MacroTokenReplaceBuffer); 359 360 temp = NULL; 361 Args->Offset[i] = PrevOffset; 362 } 363 364 /* 365 * When the replacement argument (during invocation) length = 366 * replaced parameter (in the macro function definition and 367 * its expansion) length 368 */ 369 else 370 { 371 372 /* 373 * We still need to reset the offset for each iteration even when 374 * arg and param lengths are same since any macro func invocation 375 * could use various cases for each separate arg-param pair 376 */ 377 PrevOffset = Args->Offset[i]; 378 temp = strstr (AslGbl_MacroTokenBuffer, Args->Name); 379 380 ResetHere3: 381 temp = strstr (temp, Args->Name); 382 Args->Offset[i] = strlen (AslGbl_MacroTokenBuffer) - 383 strlen (temp); 384 if (Args->Offset[i] == 0) 385 { 386 goto JumpHere3; 387 } 388 if ((strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] - 1)])) && 389 (strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] + strlen (Args->Name))]))) 390 { 391 Args->Offset[i] += 0; 392 } 393 else 394 { 395 temp += strlen (Args->Name); 396 goto ResetHere3; 397 } 398 399 JumpHere3: 400 PrReplaceData ( 401 &AslGbl_MacroTokenBuffer[Args->Offset[i]], 402 strlen (Args->Name), Token, strlen (Token)); 403 temp = NULL; 404 Args->Offset[i] = PrevOffset; 405 } 406 } 407 408 409 /******************************************************************************* 410 * 411 * FUNCTION: PrReplaceData 412 * 413 * PARAMETERS: Buffer - Original(target) buffer pointer 414 * LengthToRemove - Length to be removed from target buffer 415 * BufferToAdd - Data to be inserted into target buffer 416 * LengthToAdd - Length of BufferToAdd 417 * 418 * RETURN: Pointer to where the buffer is replaced with data 419 * 420 * DESCRIPTION: Generic buffer data replacement. 421 * 422 ******************************************************************************/ 423 424 char * 425 PrReplaceData ( 426 char *Buffer, 427 UINT32 LengthToRemove, 428 char *BufferToAdd, 429 UINT32 LengthToAdd) 430 { 431 UINT32 BufferLength; 432 433 434 /* Buffer is a string, so the length must include the terminating zero */ 435 436 BufferLength = strlen (Buffer) + 1; 437 438 if (LengthToRemove != LengthToAdd) 439 { 440 /* 441 * Move some of the existing data 442 * 1) If adding more bytes than removing, make room for the new data 443 * 2) if removing more bytes than adding, delete the extra space 444 */ 445 if (LengthToRemove > 0) 446 { 447 memmove ((Buffer + LengthToAdd), (Buffer + LengthToRemove), 448 (BufferLength - LengthToRemove)); 449 } 450 } 451 452 453 /* Now we can move in the new data */ 454 455 if (LengthToAdd > 0) 456 { 457 memmove (Buffer, BufferToAdd, LengthToAdd); 458 } 459 return (Buffer + LengthToAdd); 460 } 461 462 463 /******************************************************************************* 464 * 465 * FUNCTION: PrOpenIncludeFile 466 * 467 * PARAMETERS: Filename - Filename or pathname for include file 468 * 469 * RETURN: None. 470 * 471 * DESCRIPTION: Open an include file and push it on the input file stack. 472 * 473 ******************************************************************************/ 474 475 FILE * 476 PrOpenIncludeFile ( 477 char *Filename, 478 char *OpenMode, 479 char **FullPathname) 480 { 481 FILE *IncludeFile; 482 ASL_INCLUDE_DIR *NextDir; 483 484 485 /* Start the actual include file on the next line */ 486 487 AslGbl_CurrentLineOffset++; 488 489 /* Attempt to open the include file */ 490 /* If the file specifies an absolute path, just open it */ 491 492 if ((Filename[0] == '/') || 493 (Filename[0] == '\\') || 494 (Filename[1] == ':')) 495 { 496 IncludeFile = PrOpenIncludeWithPrefix ( 497 "", Filename, OpenMode, FullPathname); 498 if (!IncludeFile) 499 { 500 goto ErrorExit; 501 } 502 return (IncludeFile); 503 } 504 505 /* 506 * The include filename is not an absolute path. 507 * 508 * First, search for the file within the "local" directory -- meaning 509 * the same directory that contains the source file. 510 * 511 * Construct the file pathname from the global directory name. 512 */ 513 IncludeFile = PrOpenIncludeWithPrefix ( 514 AslGbl_DirectoryPath, Filename, OpenMode, FullPathname); 515 if (IncludeFile) 516 { 517 return (IncludeFile); 518 } 519 520 /* 521 * Second, search for the file within the (possibly multiple) 522 * directories specified by the -I option on the command line. 523 */ 524 NextDir = AslGbl_IncludeDirList; 525 while (NextDir) 526 { 527 IncludeFile = PrOpenIncludeWithPrefix ( 528 NextDir->Dir, Filename, OpenMode, FullPathname); 529 if (IncludeFile) 530 { 531 return (IncludeFile); 532 } 533 534 NextDir = NextDir->Next; 535 } 536 537 /* We could not open the include file after trying very hard */ 538 539 ErrorExit: 540 snprintf (AslGbl_MainTokenBuffer, ASL_DEFAULT_LINE_BUFFER_SIZE, "%s, %s", 541 Filename, strerror (errno)); 542 PrError (ASL_ERROR, ASL_MSG_INCLUDE_FILE_OPEN, 0); 543 return (NULL); 544 } 545 546 547 /******************************************************************************* 548 * 549 * FUNCTION: FlOpenIncludeWithPrefix 550 * 551 * PARAMETERS: PrefixDir - Prefix directory pathname. Can be a zero 552 * length string. 553 * Filename - The include filename from the source ASL. 554 * 555 * RETURN: Valid file descriptor if successful. Null otherwise. 556 * 557 * DESCRIPTION: Open an include file and push it on the input file stack. 558 * 559 ******************************************************************************/ 560 561 FILE * 562 PrOpenIncludeWithPrefix ( 563 char *PrefixDir, 564 char *Filename, 565 char *OpenMode, 566 char **FullPathname) 567 { 568 FILE *IncludeFile; 569 char *Pathname; 570 571 572 /* Build the full pathname to the file */ 573 574 Pathname = FlMergePathnames (PrefixDir, Filename); 575 576 DbgPrint (ASL_PARSE_OUTPUT, PR_PREFIX_ID 577 "Include: Opening file - \"%s\"\n", 578 AslGbl_CurrentLineNumber, Pathname); 579 580 /* Attempt to open the file, push if successful */ 581 582 IncludeFile = fopen (Pathname, OpenMode); 583 if (!IncludeFile) 584 { 585 return (NULL); 586 } 587 588 /* Push the include file on the open input file stack */ 589 590 PrPushInputFileStack (IncludeFile, Pathname); 591 *FullPathname = Pathname; 592 return (IncludeFile); 593 } 594 595 596 /******************************************************************************* 597 * 598 * FUNCTION: AslPushInputFileStack 599 * 600 * PARAMETERS: InputFile - Open file pointer 601 * Filename - Name of the file 602 * 603 * RETURN: None 604 * 605 * DESCRIPTION: Push the InputFile onto the file stack, and point the parser 606 * to this file. Called when an include file is successfully 607 * opened. 608 * 609 ******************************************************************************/ 610 611 void 612 PrPushInputFileStack ( 613 FILE *InputFile, 614 char *Filename) 615 { 616 PR_FILE_NODE *Fnode; 617 618 619 AslGbl_HasIncludeFiles = TRUE; 620 621 /* Save the current state in an Fnode */ 622 623 Fnode = UtLocalCalloc (sizeof (PR_FILE_NODE)); 624 625 Fnode->File = AslGbl_Files[ASL_FILE_INPUT].Handle; 626 Fnode->Next = AslGbl_InputFileList; 627 Fnode->Filename = AslGbl_Files[ASL_FILE_INPUT].Filename; 628 Fnode->CurrentLineNumber = AslGbl_CurrentLineNumber; 629 630 /* Push it on the stack */ 631 632 AslGbl_InputFileList = Fnode; 633 634 DbgPrint (ASL_PARSE_OUTPUT, PR_PREFIX_ID 635 "Push InputFile Stack: handle %p\n\n", 636 AslGbl_CurrentLineNumber, InputFile); 637 638 /* Reset the global line count and filename */ 639 640 AslGbl_Files[ASL_FILE_INPUT].Filename = 641 UtLocalCacheCalloc (strlen (Filename) + 1); 642 strcpy (AslGbl_Files[ASL_FILE_INPUT].Filename, Filename); 643 644 AslGbl_Files[ASL_FILE_INPUT].Handle = InputFile; 645 AslGbl_CurrentLineNumber = 1; 646 647 /* Emit a new #line directive for the include file */ 648 649 FlPrintFile (ASL_FILE_PREPROCESSOR, "#line %u \"%s\"\n", 1, Filename); 650 } 651 652 653 /******************************************************************************* 654 * 655 * FUNCTION: AslPopInputFileStack 656 * 657 * PARAMETERS: None 658 * 659 * RETURN: 0 if a node was popped, -1 otherwise 660 * 661 * DESCRIPTION: Pop the top of the input file stack and point the parser to 662 * the saved parse buffer contained in the fnode. Also, set the 663 * global line counters to the saved values. This function is 664 * called when an include file reaches EOF. 665 * 666 ******************************************************************************/ 667 668 BOOLEAN 669 PrPopInputFileStack ( 670 void) 671 { 672 PR_FILE_NODE *Fnode; 673 674 675 Fnode = AslGbl_InputFileList; 676 DbgPrint (ASL_PARSE_OUTPUT, "\n" PR_PREFIX_ID 677 "Pop InputFile Stack, Fnode %p\n\n", 678 AslGbl_CurrentLineNumber, Fnode); 679 680 if (!Fnode) 681 { 682 return (FALSE); 683 } 684 685 /* Close the current include file */ 686 687 fclose (AslGbl_Files[ASL_FILE_INPUT].Handle); 688 689 /* Update the top-of-stack */ 690 691 AslGbl_InputFileList = Fnode->Next; 692 693 /* Reset global line counter and filename */ 694 695 AslGbl_Files[ASL_FILE_INPUT].Filename = Fnode->Filename; 696 AslGbl_Files[ASL_FILE_INPUT].Handle = Fnode->File; 697 AslGbl_CurrentLineNumber = Fnode->CurrentLineNumber; 698 699 /* Emit a new #line directive after the include file */ 700 701 FlPrintFile (ASL_FILE_PREPROCESSOR, "#line %u \"%s\"\n", 702 AslGbl_CurrentLineNumber, Fnode->Filename); 703 704 /* All done with this node */ 705 706 ACPI_FREE (Fnode); 707 return (TRUE); 708 } 709