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