prutils.c revision 1.16 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