xref: /netbsd-src/sys/external/bsd/acpica/dist/compiler/prutils.c (revision 987b04d624d6d5e25e3e80d683a4ebe80fe47dcf)
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