prutils.c revision 1.1.1.15 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 sprintf (AslGbl_MainTokenBuffer, "%s, %s", Filename, strerror (errno));
565 PrError (ASL_ERROR, ASL_MSG_INCLUDE_FILE_OPEN, 0);
566 return (NULL);
567 }
568
569
570 /*******************************************************************************
571 *
572 * FUNCTION: FlOpenIncludeWithPrefix
573 *
574 * PARAMETERS: PrefixDir - Prefix directory pathname. Can be a zero
575 * length string.
576 * Filename - The include filename from the source ASL.
577 *
578 * RETURN: Valid file descriptor if successful. Null otherwise.
579 *
580 * DESCRIPTION: Open an include file and push it on the input file stack.
581 *
582 ******************************************************************************/
583
584 FILE *
585 PrOpenIncludeWithPrefix (
586 char *PrefixDir,
587 char *Filename,
588 char *OpenMode,
589 char **FullPathname)
590 {
591 FILE *IncludeFile;
592 char *Pathname;
593
594
595 /* Build the full pathname to the file */
596
597 Pathname = FlMergePathnames (PrefixDir, Filename);
598
599 DbgPrint (ASL_PARSE_OUTPUT, PR_PREFIX_ID
600 "Include: Opening file - \"%s\"\n",
601 AslGbl_CurrentLineNumber, Pathname);
602
603 /* Attempt to open the file, push if successful */
604
605 IncludeFile = fopen (Pathname, OpenMode);
606 if (!IncludeFile)
607 {
608 return (NULL);
609 }
610
611 /* Push the include file on the open input file stack */
612
613 PrPushInputFileStack (IncludeFile, Pathname);
614 *FullPathname = Pathname;
615 return (IncludeFile);
616 }
617
618
619 /*******************************************************************************
620 *
621 * FUNCTION: AslPushInputFileStack
622 *
623 * PARAMETERS: InputFile - Open file pointer
624 * Filename - Name of the file
625 *
626 * RETURN: None
627 *
628 * DESCRIPTION: Push the InputFile onto the file stack, and point the parser
629 * to this file. Called when an include file is successfully
630 * opened.
631 *
632 ******************************************************************************/
633
634 void
635 PrPushInputFileStack (
636 FILE *InputFile,
637 char *Filename)
638 {
639 PR_FILE_NODE *Fnode;
640
641
642 AslGbl_HasIncludeFiles = TRUE;
643
644 /* Save the current state in an Fnode */
645
646 Fnode = UtLocalCalloc (sizeof (PR_FILE_NODE));
647
648 Fnode->File = AslGbl_Files[ASL_FILE_INPUT].Handle;
649 Fnode->Next = AslGbl_InputFileList;
650 Fnode->Filename = AslGbl_Files[ASL_FILE_INPUT].Filename;
651 Fnode->CurrentLineNumber = AslGbl_CurrentLineNumber;
652
653 /* Push it on the stack */
654
655 AslGbl_InputFileList = Fnode;
656
657 DbgPrint (ASL_PARSE_OUTPUT, PR_PREFIX_ID
658 "Push InputFile Stack: handle %p\n\n",
659 AslGbl_CurrentLineNumber, InputFile);
660
661 /* Reset the global line count and filename */
662
663 AslGbl_Files[ASL_FILE_INPUT].Filename =
664 UtLocalCacheCalloc (strlen (Filename) + 1);
665 strcpy (AslGbl_Files[ASL_FILE_INPUT].Filename, Filename);
666
667 AslGbl_Files[ASL_FILE_INPUT].Handle = InputFile;
668 AslGbl_CurrentLineNumber = 1;
669
670 /* Emit a new #line directive for the include file */
671
672 FlPrintFile (ASL_FILE_PREPROCESSOR, "#line %u \"%s\"\n", 1, Filename);
673 }
674
675
676 /*******************************************************************************
677 *
678 * FUNCTION: AslPopInputFileStack
679 *
680 * PARAMETERS: None
681 *
682 * RETURN: 0 if a node was popped, -1 otherwise
683 *
684 * DESCRIPTION: Pop the top of the input file stack and point the parser to
685 * the saved parse buffer contained in the fnode. Also, set the
686 * global line counters to the saved values. This function is
687 * called when an include file reaches EOF.
688 *
689 ******************************************************************************/
690
691 BOOLEAN
692 PrPopInputFileStack (
693 void)
694 {
695 PR_FILE_NODE *Fnode;
696
697
698 Fnode = AslGbl_InputFileList;
699 DbgPrint (ASL_PARSE_OUTPUT, "\n" PR_PREFIX_ID
700 "Pop InputFile Stack, Fnode %p\n\n",
701 AslGbl_CurrentLineNumber, Fnode);
702
703 if (!Fnode)
704 {
705 return (FALSE);
706 }
707
708 /* Close the current include file */
709
710 fclose (AslGbl_Files[ASL_FILE_INPUT].Handle);
711
712 /* Update the top-of-stack */
713
714 AslGbl_InputFileList = Fnode->Next;
715
716 /* Reset global line counter and filename */
717
718 AslGbl_Files[ASL_FILE_INPUT].Filename = Fnode->Filename;
719 AslGbl_Files[ASL_FILE_INPUT].Handle = Fnode->File;
720 AslGbl_CurrentLineNumber = Fnode->CurrentLineNumber;
721
722 /* Emit a new #line directive after the include file */
723
724 FlPrintFile (ASL_FILE_PREPROCESSOR, "#line %u \"%s\"\n",
725 AslGbl_CurrentLineNumber, Fnode->Filename);
726
727 /* All done with this node */
728
729 ACPI_FREE (Fnode);
730 return (TRUE);
731 }
732