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 if (temp == NULL) 227 { 228 return; 229 } 230 231 ResetHere1: 232 temp = strstr (temp, Args->Name); 233 if (temp == NULL) 234 { 235 return; 236 } 237 Args->Offset[i] = strlen (AslGbl_MacroTokenBuffer) - 238 strlen (temp); 239 if (Args->Offset[i] == 0) 240 { 241 goto JumpHere1; 242 } 243 if ((strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] - 1)])) && 244 (strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] + strlen (Args->Name))]))) 245 { 246 Args->Offset[i] += 0; 247 } 248 else 249 { 250 temp += strlen (Args->Name); 251 goto ResetHere1; 252 } 253 254 /* 255 * For now, we simply set the extra char positions (generated 256 * due to longer name replaced by shorter name) to whitespace 257 * chars so it will be ignored during compilation 258 */ 259 JumpHere1: 260 b = strlen (Token) + Args->Offset[i]; 261 memset (&AslGbl_MacroTokenBuffer[b], ' ', Diff1); 262 263 # if 0 264 265 /* Work in progress as of 03/08/2023 - experimental 'if' block 266 * to test code for removing extra whitespaces from the macro 267 * replacement when replacement arg < replaced param 268 */ 269 char Buff[8192]; 270 /* char* Replace; */ 271 /* Replace = Buff; */ 272 273 for (j = 0; j < strlen (AslGbl_MacroTokenBuffer); j++) 274 { 275 Buff[j] = AslGbl_MacroTokenBuffer[j]; 276 } 277 Buff[strlen (AslGbl_MacroTokenBuffer)] = '\0'; 278 /* fprintf(stderr, "Buff: %s\n", Buff); */ 279 280 UINT32 len = strlen (Buff); 281 282 for (j = 0; j < len; j++) 283 { 284 if (Buff[0] == ' ') 285 { 286 for (j = 0; j < (len - 1); j++) 287 { 288 Buff[j] = Buff[j + 1]; 289 } 290 Buff[j] = '\0'; 291 len--; 292 j = -1; 293 continue; 294 } 295 296 if (Buff[j] == ' ' && Buff[j + 1] == ' ') 297 { 298 for (k = 0; k < (len - 1); k++) 299 { 300 Buff[j] = Buff[j + 1]; 301 } 302 Buff[j] = '\0'; 303 len--; 304 j--; 305 } 306 } 307 /* fprintf(stderr, "Buff: %s\n", Buff); */ 308 309 for (k = 0; k < strlen (Buff); k++) 310 { 311 AslGbl_MacroTokenBuffer[k] = Buff[k]; 312 } 313 #endif 314 315 PrReplaceData ( 316 &AslGbl_MacroTokenBuffer[Args->Offset[i]], 317 strlen (Token), Token, strlen (Token)); 318 319 temp = NULL; 320 Args->Offset[i] = PrevOffset; 321 } 322 323 /* 324 * When the replacement argument (during invocation) length 325 * > replaced parameter (in the macro function definition 326 * and its expansion) length 327 */ 328 else if (Diff2 != 0) 329 { 330 /* Doing the same thing with offset value as for prev case */ 331 332 PrevOffset = Args->Offset[i]; 333 temp = strstr (AslGbl_MacroTokenBuffer, Args->Name); 334 if (temp == NULL) 335 { 336 return; 337 } 338 339 ResetHere2: 340 temp = strstr (temp, Args->Name); 341 if (temp == NULL) 342 { 343 return; 344 } 345 Args->Offset[i] = strlen (AslGbl_MacroTokenBuffer) - 346 strlen (temp); 347 if (Args->Offset[i] == 0) 348 { 349 goto JumpHere2; 350 } 351 if ((strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] - 1)])) && 352 (strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] + strlen (Args->Name))]))) 353 { 354 Args->Offset[i] += 0; 355 } 356 else 357 { 358 temp+= strlen (Args->Name); 359 goto ResetHere2; 360 } 361 362 /* 363 * We will need to allocate some extra space in our buffer to 364 * accommodate the increase in the replacement string length 365 * over the shorter outgoing arg string and do the replacement 366 * at the correct offset value which is resetted every iteration 367 */ 368 JumpHere2: 369 strncpy (AslGbl_MacroTokenReplaceBuffer, AslGbl_MacroTokenBuffer, Args->Offset[i]); 370 strcat (AslGbl_MacroTokenReplaceBuffer, Token); 371 strcat (AslGbl_MacroTokenReplaceBuffer, (AslGbl_MacroTokenBuffer + 372 (Args->Offset[i] + strlen (Args->Name)))); 373 374 strcpy (AslGbl_MacroTokenBuffer, AslGbl_MacroTokenReplaceBuffer); 375 376 temp = NULL; 377 Args->Offset[i] = PrevOffset; 378 } 379 380 /* 381 * When the replacement argument (during invocation) length = 382 * replaced parameter (in the macro function definition and 383 * its expansion) length 384 */ 385 else 386 { 387 388 /* 389 * We still need to reset the offset for each iteration even when 390 * arg and param lengths are same since any macro func invocation 391 * could use various cases for each separate arg-param pair 392 */ 393 PrevOffset = Args->Offset[i]; 394 temp = strstr (AslGbl_MacroTokenBuffer, Args->Name); 395 if (temp == NULL) 396 { 397 return; 398 } 399 400 ResetHere3: 401 temp = strstr (temp, Args->Name); 402 if (temp == NULL) 403 { 404 return; 405 } 406 Args->Offset[i] = strlen (AslGbl_MacroTokenBuffer) - 407 strlen (temp); 408 if (Args->Offset[i] == 0) 409 { 410 goto JumpHere3; 411 } 412 if ((strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] - 1)])) && 413 (strchr (macro_sep, AslGbl_MacroTokenBuffer[(Args->Offset[i] + strlen (Args->Name))]))) 414 { 415 Args->Offset[i] += 0; 416 } 417 else 418 { 419 temp += strlen (Args->Name); 420 goto ResetHere3; 421 } 422 423 JumpHere3: 424 PrReplaceData ( 425 &AslGbl_MacroTokenBuffer[Args->Offset[i]], 426 strlen (Args->Name), Token, strlen (Token)); 427 temp = NULL; 428 Args->Offset[i] = PrevOffset; 429 } 430 } 431 432 433 /******************************************************************************* 434 * 435 * FUNCTION: PrReplaceData 436 * 437 * PARAMETERS: Buffer - Original(target) buffer pointer 438 * LengthToRemove - Length to be removed from target buffer 439 * BufferToAdd - Data to be inserted into target buffer 440 * LengthToAdd - Length of BufferToAdd 441 * 442 * RETURN: Pointer to where the buffer is replaced with data 443 * 444 * DESCRIPTION: Generic buffer data replacement. 445 * 446 ******************************************************************************/ 447 448 char * 449 PrReplaceData ( 450 char *Buffer, 451 UINT32 LengthToRemove, 452 char *BufferToAdd, 453 UINT32 LengthToAdd) 454 { 455 UINT32 BufferLength; 456 457 458 /* Buffer is a string, so the length must include the terminating zero */ 459 460 BufferLength = strlen (Buffer) + 1; 461 462 if (LengthToRemove != LengthToAdd) 463 { 464 /* 465 * Move some of the existing data 466 * 1) If adding more bytes than removing, make room for the new data 467 * 2) if removing more bytes than adding, delete the extra space 468 */ 469 if (LengthToRemove > 0) 470 { 471 memmove ((Buffer + LengthToAdd), (Buffer + LengthToRemove), 472 (BufferLength - LengthToRemove)); 473 } 474 } 475 476 477 /* Now we can move in the new data */ 478 479 if (LengthToAdd > 0) 480 { 481 memmove (Buffer, BufferToAdd, LengthToAdd); 482 } 483 return (Buffer + LengthToAdd); 484 } 485 486 487 /******************************************************************************* 488 * 489 * FUNCTION: PrOpenIncludeFile 490 * 491 * PARAMETERS: Filename - Filename or pathname for include file 492 * 493 * RETURN: None. 494 * 495 * DESCRIPTION: Open an include file and push it on the input file stack. 496 * 497 ******************************************************************************/ 498 499 FILE * 500 PrOpenIncludeFile ( 501 char *Filename, 502 char *OpenMode, 503 char **FullPathname) 504 { 505 FILE *IncludeFile; 506 ASL_INCLUDE_DIR *NextDir; 507 508 509 /* Start the actual include file on the next line */ 510 511 AslGbl_CurrentLineOffset++; 512 513 /* Attempt to open the include file */ 514 /* If the file specifies an absolute path, just open it */ 515 516 if ((Filename[0] == '/') || 517 (Filename[0] == '\\') || 518 (Filename[1] == ':')) 519 { 520 IncludeFile = PrOpenIncludeWithPrefix ( 521 "", Filename, OpenMode, FullPathname); 522 if (!IncludeFile) 523 { 524 goto ErrorExit; 525 } 526 return (IncludeFile); 527 } 528 529 /* 530 * The include filename is not an absolute path. 531 * 532 * First, search for the file within the "local" directory -- meaning 533 * the same directory that contains the source file. 534 * 535 * Construct the file pathname from the global directory name. 536 */ 537 IncludeFile = PrOpenIncludeWithPrefix ( 538 AslGbl_DirectoryPath, Filename, OpenMode, FullPathname); 539 if (IncludeFile) 540 { 541 return (IncludeFile); 542 } 543 544 /* 545 * Second, search for the file within the (possibly multiple) 546 * directories specified by the -I option on the command line. 547 */ 548 NextDir = AslGbl_IncludeDirList; 549 while (NextDir) 550 { 551 IncludeFile = PrOpenIncludeWithPrefix ( 552 NextDir->Dir, Filename, OpenMode, FullPathname); 553 if (IncludeFile) 554 { 555 return (IncludeFile); 556 } 557 558 NextDir = NextDir->Next; 559 } 560 561 /* We could not open the include file after trying very hard */ 562 563 ErrorExit: 564 snprintf (AslGbl_MainTokenBuffer, ASL_DEFAULT_LINE_BUFFER_SIZE, "%s, %s", 565 Filename, strerror (errno)); 566 PrError (ASL_ERROR, ASL_MSG_INCLUDE_FILE_OPEN, 0); 567 return (NULL); 568 } 569 570 571 /******************************************************************************* 572 * 573 * FUNCTION: FlOpenIncludeWithPrefix 574 * 575 * PARAMETERS: PrefixDir - Prefix directory pathname. Can be a zero 576 * length string. 577 * Filename - The include filename from the source ASL. 578 * 579 * RETURN: Valid file descriptor if successful. Null otherwise. 580 * 581 * DESCRIPTION: Open an include file and push it on the input file stack. 582 * 583 ******************************************************************************/ 584 585 FILE * 586 PrOpenIncludeWithPrefix ( 587 char *PrefixDir, 588 char *Filename, 589 char *OpenMode, 590 char **FullPathname) 591 { 592 FILE *IncludeFile; 593 char *Pathname; 594 595 596 /* Build the full pathname to the file */ 597 598 Pathname = FlMergePathnames (PrefixDir, Filename); 599 600 DbgPrint (ASL_PARSE_OUTPUT, PR_PREFIX_ID 601 "Include: Opening file - \"%s\"\n", 602 AslGbl_CurrentLineNumber, Pathname); 603 604 /* Attempt to open the file, push if successful */ 605 606 IncludeFile = fopen (Pathname, OpenMode); 607 if (!IncludeFile) 608 { 609 return (NULL); 610 } 611 612 /* Push the include file on the open input file stack */ 613 614 PrPushInputFileStack (IncludeFile, Pathname); 615 *FullPathname = Pathname; 616 return (IncludeFile); 617 } 618 619 620 /******************************************************************************* 621 * 622 * FUNCTION: AslPushInputFileStack 623 * 624 * PARAMETERS: InputFile - Open file pointer 625 * Filename - Name of the file 626 * 627 * RETURN: None 628 * 629 * DESCRIPTION: Push the InputFile onto the file stack, and point the parser 630 * to this file. Called when an include file is successfully 631 * opened. 632 * 633 ******************************************************************************/ 634 635 void 636 PrPushInputFileStack ( 637 FILE *InputFile, 638 char *Filename) 639 { 640 PR_FILE_NODE *Fnode; 641 642 643 AslGbl_HasIncludeFiles = TRUE; 644 645 /* Save the current state in an Fnode */ 646 647 Fnode = UtLocalCalloc (sizeof (PR_FILE_NODE)); 648 649 Fnode->File = AslGbl_Files[ASL_FILE_INPUT].Handle; 650 Fnode->Next = AslGbl_InputFileList; 651 Fnode->Filename = AslGbl_Files[ASL_FILE_INPUT].Filename; 652 Fnode->CurrentLineNumber = AslGbl_CurrentLineNumber; 653 654 /* Push it on the stack */ 655 656 AslGbl_InputFileList = Fnode; 657 658 DbgPrint (ASL_PARSE_OUTPUT, PR_PREFIX_ID 659 "Push InputFile Stack: handle %p\n\n", 660 AslGbl_CurrentLineNumber, InputFile); 661 662 /* Reset the global line count and filename */ 663 664 AslGbl_Files[ASL_FILE_INPUT].Filename = 665 UtLocalCacheCalloc (strlen (Filename) + 1); 666 strcpy (AslGbl_Files[ASL_FILE_INPUT].Filename, Filename); 667 668 AslGbl_Files[ASL_FILE_INPUT].Handle = InputFile; 669 AslGbl_CurrentLineNumber = 1; 670 671 /* Emit a new #line directive for the include file */ 672 673 FlPrintFile (ASL_FILE_PREPROCESSOR, "#line %u \"%s\"\n", 1, Filename); 674 } 675 676 677 /******************************************************************************* 678 * 679 * FUNCTION: AslPopInputFileStack 680 * 681 * PARAMETERS: None 682 * 683 * RETURN: 0 if a node was popped, -1 otherwise 684 * 685 * DESCRIPTION: Pop the top of the input file stack and point the parser to 686 * the saved parse buffer contained in the fnode. Also, set the 687 * global line counters to the saved values. This function is 688 * called when an include file reaches EOF. 689 * 690 ******************************************************************************/ 691 692 BOOLEAN 693 PrPopInputFileStack ( 694 void) 695 { 696 PR_FILE_NODE *Fnode; 697 698 699 Fnode = AslGbl_InputFileList; 700 DbgPrint (ASL_PARSE_OUTPUT, "\n" PR_PREFIX_ID 701 "Pop InputFile Stack, Fnode %p\n\n", 702 AslGbl_CurrentLineNumber, Fnode); 703 704 if (!Fnode) 705 { 706 return (FALSE); 707 } 708 709 /* Close the current include file */ 710 711 fclose (AslGbl_Files[ASL_FILE_INPUT].Handle); 712 713 /* Update the top-of-stack */ 714 715 AslGbl_InputFileList = Fnode->Next; 716 717 /* Reset global line counter and filename */ 718 719 AslGbl_Files[ASL_FILE_INPUT].Filename = Fnode->Filename; 720 AslGbl_Files[ASL_FILE_INPUT].Handle = Fnode->File; 721 AslGbl_CurrentLineNumber = Fnode->CurrentLineNumber; 722 723 /* Emit a new #line directive after the include file */ 724 725 FlPrintFile (ASL_FILE_PREPROCESSOR, "#line %u \"%s\"\n", 726 AslGbl_CurrentLineNumber, Fnode->Filename); 727 728 /* All done with this node */ 729 730 ACPI_FREE (Fnode); 731 return (TRUE); 732 } 733