| @@ -1,1119 +1,1119 @@ | | | @@ -1,1119 +1,1119 @@ |
1 | /* $NetBSD: parse.c,v 1.415 2020/10/31 21:52:56 rillig Exp $ */ | | 1 | /* $NetBSD: parse.c,v 1.416 2020/10/31 23:01:23 rillig Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1988, 1989, 1990, 1993 | | 4 | * Copyright (c) 1988, 1989, 1990, 1993 |
5 | * The Regents of the University of California. All rights reserved. | | 5 | * The Regents of the University of California. All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to Berkeley by | | 7 | * This code is derived from software contributed to Berkeley by |
8 | * Adam de Boor. | | 8 | * Adam de Boor. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | 15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the | | 16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. | | 17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its contributors | | 18 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software | | 19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. | | 20 | * without specific prior written permission. |
21 | * | | 21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. | | 32 | * SUCH DAMAGE. |
33 | */ | | 33 | */ |
34 | | | 34 | |
35 | /* | | 35 | /* |
36 | * Copyright (c) 1989 by Berkeley Softworks | | 36 | * Copyright (c) 1989 by Berkeley Softworks |
37 | * All rights reserved. | | 37 | * All rights reserved. |
38 | * | | 38 | * |
39 | * This code is derived from software contributed to Berkeley by | | 39 | * This code is derived from software contributed to Berkeley by |
40 | * Adam de Boor. | | 40 | * Adam de Boor. |
41 | * | | 41 | * |
42 | * Redistribution and use in source and binary forms, with or without | | 42 | * Redistribution and use in source and binary forms, with or without |
43 | * modification, are permitted provided that the following conditions | | 43 | * modification, are permitted provided that the following conditions |
44 | * are met: | | 44 | * are met: |
45 | * 1. Redistributions of source code must retain the above copyright | | 45 | * 1. Redistributions of source code must retain the above copyright |
46 | * notice, this list of conditions and the following disclaimer. | | 46 | * notice, this list of conditions and the following disclaimer. |
47 | * 2. Redistributions in binary form must reproduce the above copyright | | 47 | * 2. Redistributions in binary form must reproduce the above copyright |
48 | * notice, this list of conditions and the following disclaimer in the | | 48 | * notice, this list of conditions and the following disclaimer in the |
49 | * documentation and/or other materials provided with the distribution. | | 49 | * documentation and/or other materials provided with the distribution. |
50 | * 3. All advertising materials mentioning features or use of this software | | 50 | * 3. All advertising materials mentioning features or use of this software |
51 | * must display the following acknowledgement: | | 51 | * must display the following acknowledgement: |
52 | * This product includes software developed by the University of | | 52 | * This product includes software developed by the University of |
53 | * California, Berkeley and its contributors. | | 53 | * California, Berkeley and its contributors. |
54 | * 4. Neither the name of the University nor the names of its contributors | | 54 | * 4. Neither the name of the University nor the names of its contributors |
55 | * may be used to endorse or promote products derived from this software | | 55 | * may be used to endorse or promote products derived from this software |
56 | * without specific prior written permission. | | 56 | * without specific prior written permission. |
57 | * | | 57 | * |
58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
68 | * SUCH DAMAGE. | | 68 | * SUCH DAMAGE. |
69 | */ | | 69 | */ |
70 | | | 70 | |
71 | /* | | 71 | /* |
72 | * Parsing of makefiles. | | 72 | * Parsing of makefiles. |
73 | * | | 73 | * |
74 | * Parse_File is the main entry point and controls most of the other | | 74 | * Parse_File is the main entry point and controls most of the other |
75 | * functions in this module. | | 75 | * functions in this module. |
76 | * | | 76 | * |
77 | * The directories for the .include "..." directive are kept in | | 77 | * The directories for the .include "..." directive are kept in |
78 | * 'parseIncPath', while those for .include <...> are kept in 'sysIncPath'. | | 78 | * 'parseIncPath', while those for .include <...> are kept in 'sysIncPath'. |
79 | * The targets currently being defined are kept in 'targets'. | | 79 | * The targets currently being defined are kept in 'targets'. |
80 | * | | 80 | * |
81 | * Interface: | | 81 | * Interface: |
82 | * Parse_Init Initialize the module | | 82 | * Parse_Init Initialize the module |
83 | * | | 83 | * |
84 | * Parse_End Clean up the module | | 84 | * Parse_End Clean up the module |
85 | * | | 85 | * |
86 | * Parse_File Parse a top-level makefile. Included files are | | 86 | * Parse_File Parse a top-level makefile. Included files are |
87 | * handled by Parse_include_file though. | | 87 | * handled by Parse_include_file though. |
88 | * | | 88 | * |
89 | * Parse_IsVar Return TRUE if the given line is a variable | | 89 | * Parse_IsVar Return TRUE if the given line is a variable |
90 | * assignment. Used by MainParseArgs to determine if | | 90 | * assignment. Used by MainParseArgs to determine if |
91 | * an argument is a target or a variable assignment. | | 91 | * an argument is a target or a variable assignment. |
92 | * Used internally for pretty much the same thing. | | 92 | * Used internally for pretty much the same thing. |
93 | * | | 93 | * |
94 | * Parse_Error Report a parse error, a warning or an informational | | 94 | * Parse_Error Report a parse error, a warning or an informational |
95 | * message. | | 95 | * message. |
96 | * | | 96 | * |
97 | * Parse_MainName Returns a list of the main target to create. | | 97 | * Parse_MainName Returns a list of the main target to create. |
98 | */ | | 98 | */ |
99 | | | 99 | |
100 | #include <sys/types.h> | | 100 | #include <sys/types.h> |
101 | #include <sys/mman.h> | | 101 | #include <sys/mman.h> |
102 | #include <sys/stat.h> | | 102 | #include <sys/stat.h> |
103 | #include <errno.h> | | 103 | #include <errno.h> |
104 | #include <stdarg.h> | | 104 | #include <stdarg.h> |
105 | #include <stdint.h> | | 105 | #include <stdint.h> |
106 | | | 106 | |
107 | #ifndef MAP_FILE | | 107 | #ifndef MAP_FILE |
108 | #define MAP_FILE 0 | | 108 | #define MAP_FILE 0 |
109 | #endif | | 109 | #endif |
110 | #ifndef MAP_COPY | | 110 | #ifndef MAP_COPY |
111 | #define MAP_COPY MAP_PRIVATE | | 111 | #define MAP_COPY MAP_PRIVATE |
112 | #endif | | 112 | #endif |
113 | | | 113 | |
114 | #include "make.h" | | 114 | #include "make.h" |
115 | #include "dir.h" | | 115 | #include "dir.h" |
116 | #include "job.h" | | 116 | #include "job.h" |
117 | #include "pathnames.h" | | 117 | #include "pathnames.h" |
118 | | | 118 | |
119 | /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ | | 119 | /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ |
120 | MAKE_RCSID("$NetBSD: parse.c,v 1.415 2020/10/31 21:52:56 rillig Exp $"); | | 120 | MAKE_RCSID("$NetBSD: parse.c,v 1.416 2020/10/31 23:01:23 rillig Exp $"); |
121 | | | 121 | |
122 | /* types and constants */ | | 122 | /* types and constants */ |
123 | | | 123 | |
124 | /* | | 124 | /* |
125 | * Structure for a file being read ("included file") | | 125 | * Structure for a file being read ("included file") |
126 | */ | | 126 | */ |
127 | typedef struct IFile { | | 127 | typedef struct IFile { |
128 | char *fname; /* name of file */ | | 128 | char *fname; /* name of file */ |
129 | Boolean fromForLoop; /* simulated .include by the .for loop */ | | 129 | Boolean fromForLoop; /* simulated .include by the .for loop */ |
130 | int lineno; /* current line number in file */ | | 130 | int lineno; /* current line number in file */ |
131 | int first_lineno; /* line number of start of text */ | | 131 | int first_lineno; /* line number of start of text */ |
132 | unsigned int cond_depth; /* 'if' nesting when file opened */ | | 132 | unsigned int cond_depth; /* 'if' nesting when file opened */ |
133 | Boolean depending; /* state of doing_depend on EOF */ | | 133 | Boolean depending; /* state of doing_depend on EOF */ |
134 | | | 134 | |
135 | /* The buffer from which the file's content is read. */ | | 135 | /* The buffer from which the file's content is read. */ |
136 | char *buf_freeIt; | | 136 | char *buf_freeIt; |
137 | char *buf_ptr; /* next char to be read */ | | 137 | char *buf_ptr; /* next char to be read */ |
138 | char *buf_end; | | 138 | char *buf_end; |
139 | | | 139 | |
140 | char *(*nextbuf)(void *, size_t *); /* Function to get more data */ | | 140 | char *(*nextbuf)(void *, size_t *); /* Function to get more data */ |
141 | void *nextbuf_arg; /* Opaque arg for nextbuf() */ | | 141 | void *nextbuf_arg; /* Opaque arg for nextbuf() */ |
142 | struct loadedfile *lf; /* loadedfile object, if any */ | | 142 | struct loadedfile *lf; /* loadedfile object, if any */ |
143 | } IFile; | | 143 | } IFile; |
144 | | | 144 | |
145 | /* | | 145 | /* |
146 | * Tokens for target attributes | | 146 | * Tokens for target attributes |
147 | */ | | 147 | */ |
148 | typedef enum ParseSpecial { | | 148 | typedef enum ParseSpecial { |
149 | SP_ATTRIBUTE, /* Generic attribute */ | | 149 | SP_ATTRIBUTE, /* Generic attribute */ |
150 | SP_BEGIN, /* .BEGIN */ | | 150 | SP_BEGIN, /* .BEGIN */ |
151 | SP_DEFAULT, /* .DEFAULT */ | | 151 | SP_DEFAULT, /* .DEFAULT */ |
152 | SP_DELETE_ON_ERROR, /* .DELETE_ON_ERROR */ | | 152 | SP_DELETE_ON_ERROR, /* .DELETE_ON_ERROR */ |
153 | SP_END, /* .END */ | | 153 | SP_END, /* .END */ |
154 | SP_ERROR, /* .ERROR */ | | 154 | SP_ERROR, /* .ERROR */ |
155 | SP_IGNORE, /* .IGNORE */ | | 155 | SP_IGNORE, /* .IGNORE */ |
156 | SP_INCLUDES, /* .INCLUDES; not mentioned in the manual page */ | | 156 | SP_INCLUDES, /* .INCLUDES; not mentioned in the manual page */ |
157 | SP_INTERRUPT, /* .INTERRUPT */ | | 157 | SP_INTERRUPT, /* .INTERRUPT */ |
158 | SP_LIBS, /* .LIBS; not mentioned in the manual page */ | | 158 | SP_LIBS, /* .LIBS; not mentioned in the manual page */ |
159 | SP_MAIN, /* .MAIN and we don't have anything user-specified to | | 159 | SP_MAIN, /* .MAIN and we don't have anything user-specified to |
160 | * make */ | | 160 | * make */ |
161 | SP_META, /* .META */ | | 161 | SP_META, /* .META */ |
162 | SP_MFLAGS, /* .MFLAGS or .MAKEFLAGS */ | | 162 | SP_MFLAGS, /* .MFLAGS or .MAKEFLAGS */ |
163 | SP_NOMETA, /* .NOMETA */ | | 163 | SP_NOMETA, /* .NOMETA */ |
164 | SP_NOMETA_CMP, /* .NOMETA_CMP */ | | 164 | SP_NOMETA_CMP, /* .NOMETA_CMP */ |
165 | SP_NOPATH, /* .NOPATH */ | | 165 | SP_NOPATH, /* .NOPATH */ |
166 | SP_NOT, /* Not special */ | | 166 | SP_NOT, /* Not special */ |
167 | SP_NOTPARALLEL, /* .NOTPARALLEL or .NO_PARALLEL */ | | 167 | SP_NOTPARALLEL, /* .NOTPARALLEL or .NO_PARALLEL */ |
168 | SP_NULL, /* .NULL; not mentioned in the manual page */ | | 168 | SP_NULL, /* .NULL; not mentioned in the manual page */ |
169 | SP_OBJDIR, /* .OBJDIR */ | | 169 | SP_OBJDIR, /* .OBJDIR */ |
170 | SP_ORDER, /* .ORDER */ | | 170 | SP_ORDER, /* .ORDER */ |
171 | SP_PARALLEL, /* .PARALLEL; not mentioned in the manual page */ | | 171 | SP_PARALLEL, /* .PARALLEL; not mentioned in the manual page */ |
172 | SP_PATH, /* .PATH or .PATH.suffix */ | | 172 | SP_PATH, /* .PATH or .PATH.suffix */ |
173 | SP_PHONY, /* .PHONY */ | | 173 | SP_PHONY, /* .PHONY */ |
174 | #ifdef POSIX | | 174 | #ifdef POSIX |
175 | SP_POSIX, /* .POSIX; not mentioned in the manual page */ | | 175 | SP_POSIX, /* .POSIX; not mentioned in the manual page */ |
176 | #endif | | 176 | #endif |
177 | SP_PRECIOUS, /* .PRECIOUS */ | | 177 | SP_PRECIOUS, /* .PRECIOUS */ |
178 | SP_SHELL, /* .SHELL */ | | 178 | SP_SHELL, /* .SHELL */ |
179 | SP_SILENT, /* .SILENT */ | | 179 | SP_SILENT, /* .SILENT */ |
180 | SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */ | | 180 | SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */ |
181 | SP_STALE, /* .STALE */ | | 181 | SP_STALE, /* .STALE */ |
182 | SP_SUFFIXES, /* .SUFFIXES */ | | 182 | SP_SUFFIXES, /* .SUFFIXES */ |
183 | SP_WAIT /* .WAIT */ | | 183 | SP_WAIT /* .WAIT */ |
184 | } ParseSpecial; | | 184 | } ParseSpecial; |
185 | | | 185 | |
186 | typedef List SearchPathList; | | 186 | typedef List SearchPathList; |
187 | typedef ListNode SearchPathListNode; | | 187 | typedef ListNode SearchPathListNode; |
188 | | | 188 | |
189 | /* result data */ | | 189 | /* result data */ |
190 | | | 190 | |
191 | /* | | 191 | /* |
192 | * The main target to create. This is the first target on the first | | 192 | * The main target to create. This is the first target on the first |
193 | * dependency line in the first makefile. | | 193 | * dependency line in the first makefile. |
194 | */ | | 194 | */ |
195 | static GNode *mainNode; | | 195 | static GNode *mainNode; |
196 | | | 196 | |
197 | /* eval state */ | | 197 | /* eval state */ |
198 | | | 198 | |
199 | /* During parsing, the targets from the left-hand side of the currently | | 199 | /* During parsing, the targets from the left-hand side of the currently |
200 | * active dependency line, or NULL if the current line does not belong to a | | 200 | * active dependency line, or NULL if the current line does not belong to a |
201 | * dependency line, for example because it is a variable assignment. | | 201 | * dependency line, for example because it is a variable assignment. |
202 | * | | 202 | * |
203 | * See unit-tests/deptgt.mk, keyword "parse.c:targets". */ | | 203 | * See unit-tests/deptgt.mk, keyword "parse.c:targets". */ |
204 | static GNodeList *targets; | | 204 | static GNodeList *targets; |
205 | | | 205 | |
206 | #ifdef CLEANUP | | 206 | #ifdef CLEANUP |
207 | /* All shell commands for all targets, in no particular order and possibly | | 207 | /* All shell commands for all targets, in no particular order and possibly |
208 | * with duplicates. Kept in a separate list since the commands from .USE or | | 208 | * with duplicates. Kept in a separate list since the commands from .USE or |
209 | * .USEBEFORE nodes are shared with other GNodes, thereby giving up the | | 209 | * .USEBEFORE nodes are shared with other GNodes, thereby giving up the |
210 | * easily understandable ownership over the allocated strings. */ | | 210 | * easily understandable ownership over the allocated strings. */ |
211 | static StringList *targCmds; | | 211 | static StringList *targCmds; |
212 | #endif | | 212 | #endif |
213 | | | 213 | |
214 | /* | | 214 | /* |
215 | * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER | | 215 | * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER |
216 | * seen, then set to each successive source on the line. | | 216 | * seen, then set to each successive source on the line. |
217 | */ | | 217 | */ |
218 | static GNode *order_pred; | | 218 | static GNode *order_pred; |
219 | | | 219 | |
220 | /* parser state */ | | 220 | /* parser state */ |
221 | | | 221 | |
222 | /* number of fatal errors */ | | 222 | /* number of fatal errors */ |
223 | static int fatals = 0; | | 223 | static int fatals = 0; |
224 | | | 224 | |
225 | /* | | 225 | /* |
226 | * Variables for doing includes | | 226 | * Variables for doing includes |
227 | */ | | 227 | */ |
228 | | | 228 | |
229 | /* The include chain of makefiles. At the bottom is the top-level makefile | | 229 | /* The include chain of makefiles. At the bottom is the top-level makefile |
230 | * from the command line, and on top of that, there are the included files or | | 230 | * from the command line, and on top of that, there are the included files or |
231 | * .for loops, up to and including the current file. | | 231 | * .for loops, up to and including the current file. |
232 | * | | 232 | * |
233 | * This data could be used to print stack traces on parse errors. As of | | 233 | * This data could be used to print stack traces on parse errors. As of |
234 | * 2020-09-14, this is not done though. It seems quite simple to print the | | 234 | * 2020-09-14, this is not done though. It seems quite simple to print the |
235 | * tuples (fname:lineno:fromForLoop), from top to bottom. This simple idea is | | 235 | * tuples (fname:lineno:fromForLoop), from top to bottom. This simple idea is |
236 | * made complicated by the fact that the .for loops also use this stack for | | 236 | * made complicated by the fact that the .for loops also use this stack for |
237 | * storing information. | | 237 | * storing information. |
238 | * | | 238 | * |
239 | * The lineno fields of the IFiles with fromForLoop == TRUE look confusing, | | 239 | * The lineno fields of the IFiles with fromForLoop == TRUE look confusing, |
240 | * which is demonstrated by the test 'include-main.mk'. They seem sorted | | 240 | * which is demonstrated by the test 'include-main.mk'. They seem sorted |
241 | * backwards since they tell the number of completely parsed lines, which for | | 241 | * backwards since they tell the number of completely parsed lines, which for |
242 | * a .for loop is right after the terminating .endfor. To compensate for this | | 242 | * a .for loop is right after the terminating .endfor. To compensate for this |
243 | * confusion, there is another field first_lineno pointing at the start of the | | 243 | * confusion, there is another field first_lineno pointing at the start of the |
244 | * .for loop, 1-based for human consumption. | | 244 | * .for loop, 1-based for human consumption. |
245 | * | | 245 | * |
246 | * To make the stack trace intuitive, the entry below the first .for loop must | | 246 | * To make the stack trace intuitive, the entry below the first .for loop must |
247 | * be ignored completely since neither its lineno nor its first_lineno is | | 247 | * be ignored completely since neither its lineno nor its first_lineno is |
248 | * useful. Instead, the topmost of each chain of .for loop needs to be | | 248 | * useful. Instead, the topmost of each chain of .for loop needs to be |
249 | * printed twice, once with its first_lineno and once with its lineno. | | 249 | * printed twice, once with its first_lineno and once with its lineno. |
250 | * | | 250 | * |
251 | * As of 2020-10-28, using the above rules, the stack trace for the .info line | | 251 | * As of 2020-10-28, using the above rules, the stack trace for the .info line |
252 | * in include-subsub.mk would be: | | 252 | * in include-subsub.mk would be: |
253 | * | | 253 | * |
254 | * includes[5]: include-subsub.mk:4 | | 254 | * includes[5]: include-subsub.mk:4 |
255 | * (lineno, from an .include) | | 255 | * (lineno, from an .include) |
256 | * includes[4]: include-sub.mk:32 | | 256 | * includes[4]: include-sub.mk:32 |
257 | * (lineno, from a .for loop below an .include) | | 257 | * (lineno, from a .for loop below an .include) |
258 | * includes[4]: include-sub.mk:31 | | 258 | * includes[4]: include-sub.mk:31 |
259 | * (first_lineno, from a .for loop, lineno == 32) | | 259 | * (first_lineno, from a .for loop, lineno == 32) |
260 | * includes[3]: include-sub.mk:30 | | 260 | * includes[3]: include-sub.mk:30 |
261 | * (first_lineno, from a .for loop, lineno == 33) | | 261 | * (first_lineno, from a .for loop, lineno == 33) |
262 | * includes[2]: include-sub.mk:29 | | 262 | * includes[2]: include-sub.mk:29 |
263 | * (first_lineno, from a .for loop, lineno == 34) | | 263 | * (first_lineno, from a .for loop, lineno == 34) |
264 | * includes[1]: include-sub.mk:35 | | 264 | * includes[1]: include-sub.mk:35 |
265 | * (not printed since it is below a .for loop) | | 265 | * (not printed since it is below a .for loop) |
266 | * includes[0]: include-main.mk:27 | | 266 | * includes[0]: include-main.mk:27 |
267 | */ | | 267 | */ |
268 | static Vector /* of IFile */ includes; | | 268 | static Vector /* of IFile */ includes; |
269 | | | 269 | |
270 | static IFile * | | 270 | static IFile * |
271 | GetInclude(size_t i) | | 271 | GetInclude(size_t i) |
272 | { | | 272 | { |
273 | return Vector_Get(&includes, i); | | 273 | return Vector_Get(&includes, i); |
274 | } | | 274 | } |
275 | | | 275 | |
276 | /* The file that is currently being read. */ | | 276 | /* The file that is currently being read. */ |
277 | static IFile * | | 277 | static IFile * |
278 | CurFile(void) | | 278 | CurFile(void) |
279 | { | | 279 | { |
280 | return GetInclude(includes.len - 1); | | 280 | return GetInclude(includes.len - 1); |
281 | } | | 281 | } |
282 | | | 282 | |
283 | /* include paths */ | | 283 | /* include paths */ |
284 | SearchPath *parseIncPath; /* dirs for "..." includes */ | | 284 | SearchPath *parseIncPath; /* dirs for "..." includes */ |
285 | SearchPath *sysIncPath; /* dirs for <...> includes */ | | 285 | SearchPath *sysIncPath; /* dirs for <...> includes */ |
286 | SearchPath *defSysIncPath; /* default for sysIncPath */ | | 286 | SearchPath *defSysIncPath; /* default for sysIncPath */ |
287 | | | 287 | |
288 | /* parser tables */ | | 288 | /* parser tables */ |
289 | | | 289 | |
290 | /* | | 290 | /* |
291 | * The parseKeywords table is searched using binary search when deciding | | 291 | * The parseKeywords table is searched using binary search when deciding |
292 | * if a target or source is special. The 'spec' field is the ParseSpecial | | 292 | * if a target or source is special. The 'spec' field is the ParseSpecial |
293 | * type of the keyword (SP_NOT if the keyword isn't special as a target) while | | 293 | * type of the keyword (SP_NOT if the keyword isn't special as a target) while |
294 | * the 'op' field is the operator to apply to the list of targets if the | | 294 | * the 'op' field is the operator to apply to the list of targets if the |
295 | * keyword is used as a source ("0" if the keyword isn't special as a source) | | 295 | * keyword is used as a source ("0" if the keyword isn't special as a source) |
296 | */ | | 296 | */ |
297 | static const struct { | | 297 | static const struct { |
298 | const char *name; /* Name of keyword */ | | 298 | const char *name; /* Name of keyword */ |
299 | ParseSpecial spec; /* Type when used as a target */ | | 299 | ParseSpecial spec; /* Type when used as a target */ |
300 | GNodeType op; /* Operator when used as a source */ | | 300 | GNodeType op; /* Operator when used as a source */ |
301 | } parseKeywords[] = { | | 301 | } parseKeywords[] = { |
302 | { ".BEGIN", SP_BEGIN, 0 }, | | 302 | { ".BEGIN", SP_BEGIN, 0 }, |
303 | { ".DEFAULT", SP_DEFAULT, 0 }, | | 303 | { ".DEFAULT", SP_DEFAULT, 0 }, |
304 | { ".DELETE_ON_ERROR", SP_DELETE_ON_ERROR, 0 }, | | 304 | { ".DELETE_ON_ERROR", SP_DELETE_ON_ERROR, 0 }, |
305 | { ".END", SP_END, 0 }, | | 305 | { ".END", SP_END, 0 }, |
306 | { ".ERROR", SP_ERROR, 0 }, | | 306 | { ".ERROR", SP_ERROR, 0 }, |
307 | { ".EXEC", SP_ATTRIBUTE, OP_EXEC }, | | 307 | { ".EXEC", SP_ATTRIBUTE, OP_EXEC }, |
308 | { ".IGNORE", SP_IGNORE, OP_IGNORE }, | | 308 | { ".IGNORE", SP_IGNORE, OP_IGNORE }, |
309 | { ".INCLUDES", SP_INCLUDES, 0 }, | | 309 | { ".INCLUDES", SP_INCLUDES, 0 }, |
310 | { ".INTERRUPT", SP_INTERRUPT, 0 }, | | 310 | { ".INTERRUPT", SP_INTERRUPT, 0 }, |
311 | { ".INVISIBLE", SP_ATTRIBUTE, OP_INVISIBLE }, | | 311 | { ".INVISIBLE", SP_ATTRIBUTE, OP_INVISIBLE }, |
312 | { ".JOIN", SP_ATTRIBUTE, OP_JOIN }, | | 312 | { ".JOIN", SP_ATTRIBUTE, OP_JOIN }, |
313 | { ".LIBS", SP_LIBS, 0 }, | | 313 | { ".LIBS", SP_LIBS, 0 }, |
314 | { ".MADE", SP_ATTRIBUTE, OP_MADE }, | | 314 | { ".MADE", SP_ATTRIBUTE, OP_MADE }, |
315 | { ".MAIN", SP_MAIN, 0 }, | | 315 | { ".MAIN", SP_MAIN, 0 }, |
316 | { ".MAKE", SP_ATTRIBUTE, OP_MAKE }, | | 316 | { ".MAKE", SP_ATTRIBUTE, OP_MAKE }, |
317 | { ".MAKEFLAGS", SP_MFLAGS, 0 }, | | 317 | { ".MAKEFLAGS", SP_MFLAGS, 0 }, |
318 | { ".META", SP_META, OP_META }, | | 318 | { ".META", SP_META, OP_META }, |
319 | { ".MFLAGS", SP_MFLAGS, 0 }, | | 319 | { ".MFLAGS", SP_MFLAGS, 0 }, |
320 | { ".NOMETA", SP_NOMETA, OP_NOMETA }, | | 320 | { ".NOMETA", SP_NOMETA, OP_NOMETA }, |
321 | { ".NOMETA_CMP", SP_NOMETA_CMP, OP_NOMETA_CMP }, | | 321 | { ".NOMETA_CMP", SP_NOMETA_CMP, OP_NOMETA_CMP }, |
322 | { ".NOPATH", SP_NOPATH, OP_NOPATH }, | | 322 | { ".NOPATH", SP_NOPATH, OP_NOPATH }, |
323 | { ".NOTMAIN", SP_ATTRIBUTE, OP_NOTMAIN }, | | 323 | { ".NOTMAIN", SP_ATTRIBUTE, OP_NOTMAIN }, |
324 | { ".NOTPARALLEL", SP_NOTPARALLEL, 0 }, | | 324 | { ".NOTPARALLEL", SP_NOTPARALLEL, 0 }, |
325 | { ".NO_PARALLEL", SP_NOTPARALLEL, 0 }, | | 325 | { ".NO_PARALLEL", SP_NOTPARALLEL, 0 }, |
326 | { ".NULL", SP_NULL, 0 }, | | 326 | { ".NULL", SP_NULL, 0 }, |
327 | { ".OBJDIR", SP_OBJDIR, 0 }, | | 327 | { ".OBJDIR", SP_OBJDIR, 0 }, |
328 | { ".OPTIONAL", SP_ATTRIBUTE, OP_OPTIONAL }, | | 328 | { ".OPTIONAL", SP_ATTRIBUTE, OP_OPTIONAL }, |
329 | { ".ORDER", SP_ORDER, 0 }, | | 329 | { ".ORDER", SP_ORDER, 0 }, |
330 | { ".PARALLEL", SP_PARALLEL, 0 }, | | 330 | { ".PARALLEL", SP_PARALLEL, 0 }, |
331 | { ".PATH", SP_PATH, 0 }, | | 331 | { ".PATH", SP_PATH, 0 }, |
332 | { ".PHONY", SP_PHONY, OP_PHONY }, | | 332 | { ".PHONY", SP_PHONY, OP_PHONY }, |
333 | #ifdef POSIX | | 333 | #ifdef POSIX |
334 | { ".POSIX", SP_POSIX, 0 }, | | 334 | { ".POSIX", SP_POSIX, 0 }, |
335 | #endif | | 335 | #endif |
336 | { ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS }, | | 336 | { ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS }, |
337 | { ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE }, | | 337 | { ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE }, |
338 | { ".SHELL", SP_SHELL, 0 }, | | 338 | { ".SHELL", SP_SHELL, 0 }, |
339 | { ".SILENT", SP_SILENT, OP_SILENT }, | | 339 | { ".SILENT", SP_SILENT, OP_SILENT }, |
340 | { ".SINGLESHELL", SP_SINGLESHELL, 0 }, | | 340 | { ".SINGLESHELL", SP_SINGLESHELL, 0 }, |
341 | { ".STALE", SP_STALE, 0 }, | | 341 | { ".STALE", SP_STALE, 0 }, |
342 | { ".SUFFIXES", SP_SUFFIXES, 0 }, | | 342 | { ".SUFFIXES", SP_SUFFIXES, 0 }, |
343 | { ".USE", SP_ATTRIBUTE, OP_USE }, | | 343 | { ".USE", SP_ATTRIBUTE, OP_USE }, |
344 | { ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE }, | | 344 | { ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE }, |
345 | { ".WAIT", SP_WAIT, 0 }, | | 345 | { ".WAIT", SP_WAIT, 0 }, |
346 | }; | | 346 | }; |
347 | | | 347 | |
348 | /* file loader */ | | 348 | /* file loader */ |
349 | | | 349 | |
350 | struct loadedfile { | | 350 | struct loadedfile { |
351 | const char *path; /* name, for error reports */ | | 351 | const char *path; /* name, for error reports */ |
352 | char *buf; /* contents buffer */ | | 352 | char *buf; /* contents buffer */ |
353 | size_t len; /* length of contents */ | | 353 | size_t len; /* length of contents */ |
354 | size_t maplen; /* length of mmap area, or 0 */ | | 354 | size_t maplen; /* length of mmap area, or 0 */ |
355 | Boolean used; /* XXX: have we used the data yet */ | | 355 | Boolean used; /* XXX: have we used the data yet */ |
356 | }; | | 356 | }; |
357 | | | 357 | |
358 | static struct loadedfile * | | 358 | static struct loadedfile * |
359 | loadedfile_create(const char *path) | | 359 | loadedfile_create(const char *path) |
360 | { | | 360 | { |
361 | struct loadedfile *lf; | | 361 | struct loadedfile *lf; |
362 | | | 362 | |
363 | lf = bmake_malloc(sizeof(*lf)); | | 363 | lf = bmake_malloc(sizeof(*lf)); |
364 | lf->path = path == NULL ? "(stdin)" : path; | | 364 | lf->path = path == NULL ? "(stdin)" : path; |
365 | lf->buf = NULL; | | 365 | lf->buf = NULL; |
366 | lf->len = 0; | | 366 | lf->len = 0; |
367 | lf->maplen = 0; | | 367 | lf->maplen = 0; |
368 | lf->used = FALSE; | | 368 | lf->used = FALSE; |
369 | return lf; | | 369 | return lf; |
370 | } | | 370 | } |
371 | | | 371 | |
372 | static void | | 372 | static void |
373 | loadedfile_destroy(struct loadedfile *lf) | | 373 | loadedfile_destroy(struct loadedfile *lf) |
374 | { | | 374 | { |
375 | if (lf->buf != NULL) { | | 375 | if (lf->buf != NULL) { |
376 | if (lf->maplen > 0) { | | 376 | if (lf->maplen > 0) { |
377 | munmap(lf->buf, lf->maplen); | | 377 | munmap(lf->buf, lf->maplen); |
378 | } else { | | 378 | } else { |
379 | free(lf->buf); | | 379 | free(lf->buf); |
380 | } | | 380 | } |
381 | } | | 381 | } |
382 | free(lf); | | 382 | free(lf); |
383 | } | | 383 | } |
384 | | | 384 | |
385 | /* | | 385 | /* |
386 | * nextbuf() operation for loadedfile, as needed by the weird and twisted | | 386 | * nextbuf() operation for loadedfile, as needed by the weird and twisted |
387 | * logic below. Once that's cleaned up, we can get rid of lf->used... | | 387 | * logic below. Once that's cleaned up, we can get rid of lf->used... |
388 | */ | | 388 | */ |
389 | static char * | | 389 | static char * |
390 | loadedfile_nextbuf(void *x, size_t *len) | | 390 | loadedfile_nextbuf(void *x, size_t *len) |
391 | { | | 391 | { |
392 | struct loadedfile *lf = x; | | 392 | struct loadedfile *lf = x; |
393 | | | 393 | |
394 | if (lf->used) { | | 394 | if (lf->used) { |
395 | return NULL; | | 395 | return NULL; |
396 | } | | 396 | } |
397 | lf->used = TRUE; | | 397 | lf->used = TRUE; |
398 | *len = lf->len; | | 398 | *len = lf->len; |
399 | return lf->buf; | | 399 | return lf->buf; |
400 | } | | 400 | } |
401 | | | 401 | |
402 | /* | | 402 | /* |
403 | * Try to get the size of a file. | | 403 | * Try to get the size of a file. |
404 | */ | | 404 | */ |
405 | static Boolean | | 405 | static Boolean |
406 | load_getsize(int fd, size_t *ret) | | 406 | load_getsize(int fd, size_t *ret) |
407 | { | | 407 | { |
408 | struct stat st; | | 408 | struct stat st; |
409 | | | 409 | |
410 | if (fstat(fd, &st) < 0) { | | 410 | if (fstat(fd, &st) < 0) { |
411 | return FALSE; | | 411 | return FALSE; |
412 | } | | 412 | } |
413 | | | 413 | |
414 | if (!S_ISREG(st.st_mode)) { | | 414 | if (!S_ISREG(st.st_mode)) { |
415 | return FALSE; | | 415 | return FALSE; |
416 | } | | 416 | } |
417 | | | 417 | |
418 | /* | | 418 | /* |
419 | * st_size is an off_t, which is 64 bits signed; *ret is | | 419 | * st_size is an off_t, which is 64 bits signed; *ret is |
420 | * size_t, which might be 32 bits unsigned or 64 bits | | 420 | * size_t, which might be 32 bits unsigned or 64 bits |
421 | * unsigned. Rather than being elaborate, just punt on | | 421 | * unsigned. Rather than being elaborate, just punt on |
422 | * files that are more than 2^31 bytes. We should never | | 422 | * files that are more than 2^31 bytes. We should never |
423 | * see a makefile that size in practice... | | 423 | * see a makefile that size in practice... |
424 | * | | 424 | * |
425 | * While we're at it reject negative sizes too, just in case. | | 425 | * While we're at it reject negative sizes too, just in case. |
426 | */ | | 426 | */ |
427 | if (st.st_size < 0 || st.st_size > 0x7fffffff) { | | 427 | if (st.st_size < 0 || st.st_size > 0x7fffffff) { |
428 | return FALSE; | | 428 | return FALSE; |
429 | } | | 429 | } |
430 | | | 430 | |
431 | *ret = (size_t)st.st_size; | | 431 | *ret = (size_t)st.st_size; |
432 | return TRUE; | | 432 | return TRUE; |
433 | } | | 433 | } |
434 | | | 434 | |
435 | static Boolean | | 435 | static Boolean |
436 | loadedfile_mmap(struct loadedfile *lf, int fd) | | 436 | loadedfile_mmap(struct loadedfile *lf, int fd) |
437 | { | | 437 | { |
438 | static unsigned long pagesize = 0; | | 438 | static unsigned long pagesize = 0; |
439 | | | 439 | |
440 | if (load_getsize(fd, &lf->len)) { | | 440 | if (load_getsize(fd, &lf->len)) { |
441 | | | 441 | |
442 | /* found a size, try mmap */ | | 442 | /* found a size, try mmap */ |
443 | if (pagesize == 0) | | 443 | if (pagesize == 0) |
444 | pagesize = (unsigned long)sysconf(_SC_PAGESIZE); | | 444 | pagesize = (unsigned long)sysconf(_SC_PAGESIZE); |
445 | if (pagesize == 0 || pagesize == (unsigned long)-1) { | | 445 | if (pagesize == 0 || pagesize == (unsigned long)-1) { |
446 | pagesize = 0x1000; | | 446 | pagesize = 0x1000; |
447 | } | | 447 | } |
448 | /* round size up to a page */ | | 448 | /* round size up to a page */ |
449 | lf->maplen = pagesize * ((lf->len + pagesize - 1) / pagesize); | | 449 | lf->maplen = pagesize * ((lf->len + pagesize - 1) / pagesize); |
450 | | | 450 | |
451 | /* | | 451 | /* |
452 | * XXX hack for dealing with empty files; remove when | | 452 | * XXX hack for dealing with empty files; remove when |
453 | * we're no longer limited by interfacing to the old | | 453 | * we're no longer limited by interfacing to the old |
454 | * logic elsewhere in this file. | | 454 | * logic elsewhere in this file. |
455 | */ | | 455 | */ |
456 | if (lf->maplen == 0) { | | 456 | if (lf->maplen == 0) { |
457 | lf->maplen = pagesize; | | 457 | lf->maplen = pagesize; |
458 | } | | 458 | } |
459 | | | 459 | |
460 | /* | | 460 | /* |
461 | * FUTURE: remove PROT_WRITE when the parser no longer | | 461 | * FUTURE: remove PROT_WRITE when the parser no longer |
462 | * needs to scribble on the input. | | 462 | * needs to scribble on the input. |
463 | */ | | 463 | */ |
464 | lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE, | | 464 | lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE, |
465 | MAP_FILE|MAP_COPY, fd, 0); | | 465 | MAP_FILE|MAP_COPY, fd, 0); |
466 | if (lf->buf != MAP_FAILED) { | | 466 | if (lf->buf != MAP_FAILED) { |
467 | /* succeeded */ | | 467 | /* succeeded */ |
468 | if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') { | | 468 | if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') { |
469 | char *b = bmake_malloc(lf->len + 1); | | 469 | char *b = bmake_malloc(lf->len + 1); |
470 | b[lf->len] = '\n'; | | 470 | b[lf->len] = '\n'; |
471 | memcpy(b, lf->buf, lf->len++); | | 471 | memcpy(b, lf->buf, lf->len++); |
472 | munmap(lf->buf, lf->maplen); | | 472 | munmap(lf->buf, lf->maplen); |
473 | lf->maplen = 0; | | 473 | lf->maplen = 0; |
474 | lf->buf = b; | | 474 | lf->buf = b; |
475 | } | | 475 | } |
476 | return TRUE; | | 476 | return TRUE; |
477 | } | | 477 | } |
478 | } | | 478 | } |
479 | return FALSE; | | 479 | return FALSE; |
480 | } | | 480 | } |
481 | | | 481 | |
482 | /* | | 482 | /* |
483 | * Read in a file. | | 483 | * Read in a file. |
484 | * | | 484 | * |
485 | * Until the path search logic can be moved under here instead of | | 485 | * Until the path search logic can be moved under here instead of |
486 | * being in the caller in another source file, we need to have the fd | | 486 | * being in the caller in another source file, we need to have the fd |
487 | * passed in already open. Bleh. | | 487 | * passed in already open. Bleh. |
488 | * | | 488 | * |
489 | * If the path is NULL use stdin and (to insure against fd leaks) | | 489 | * If the path is NULL use stdin and (to insure against fd leaks) |
490 | * assert that the caller passed in -1. | | 490 | * assert that the caller passed in -1. |
491 | */ | | 491 | */ |
492 | static struct loadedfile * | | 492 | static struct loadedfile * |
493 | loadfile(const char *path, int fd) | | 493 | loadfile(const char *path, int fd) |
494 | { | | 494 | { |
495 | struct loadedfile *lf; | | 495 | struct loadedfile *lf; |
496 | ssize_t result; | | 496 | ssize_t result; |
497 | size_t bufpos; | | 497 | size_t bufpos; |
498 | | | 498 | |
499 | lf = loadedfile_create(path); | | 499 | lf = loadedfile_create(path); |
500 | | | 500 | |
501 | if (path == NULL) { | | 501 | if (path == NULL) { |
502 | assert(fd == -1); | | 502 | assert(fd == -1); |
503 | fd = STDIN_FILENO; | | 503 | fd = STDIN_FILENO; |
504 | } else { | | 504 | } else { |
505 | #if 0 /* notyet */ | | 505 | #if 0 /* notyet */ |
506 | fd = open(path, O_RDONLY); | | 506 | fd = open(path, O_RDONLY); |
507 | if (fd < 0) { | | 507 | if (fd < 0) { |
508 | ... | | 508 | ... |
509 | Error("%s: %s", path, strerror(errno)); | | 509 | Error("%s: %s", path, strerror(errno)); |
510 | exit(1); | | 510 | exit(1); |
511 | } | | 511 | } |
512 | #endif | | 512 | #endif |
513 | } | | 513 | } |
514 | | | 514 | |
515 | if (loadedfile_mmap(lf, fd)) | | 515 | if (loadedfile_mmap(lf, fd)) |
516 | goto done; | | 516 | goto done; |
517 | | | 517 | |
518 | /* cannot mmap; load the traditional way */ | | 518 | /* cannot mmap; load the traditional way */ |
519 | | | 519 | |
520 | lf->maplen = 0; | | 520 | lf->maplen = 0; |
521 | lf->len = 1024; | | 521 | lf->len = 1024; |
522 | lf->buf = bmake_malloc(lf->len); | | 522 | lf->buf = bmake_malloc(lf->len); |
523 | | | 523 | |
524 | bufpos = 0; | | 524 | bufpos = 0; |
525 | while (1) { | | 525 | while (1) { |
526 | assert(bufpos <= lf->len); | | 526 | assert(bufpos <= lf->len); |
527 | if (bufpos == lf->len) { | | 527 | if (bufpos == lf->len) { |
528 | if (lf->len > SIZE_MAX/2) { | | 528 | if (lf->len > SIZE_MAX/2) { |
529 | errno = EFBIG; | | 529 | errno = EFBIG; |
530 | Error("%s: file too large", path); | | 530 | Error("%s: file too large", path); |
531 | exit(1); | | 531 | exit(1); |
532 | } | | 532 | } |
533 | lf->len *= 2; | | 533 | lf->len *= 2; |
534 | lf->buf = bmake_realloc(lf->buf, lf->len); | | 534 | lf->buf = bmake_realloc(lf->buf, lf->len); |
535 | } | | 535 | } |
536 | assert(bufpos < lf->len); | | 536 | assert(bufpos < lf->len); |
537 | result = read(fd, lf->buf + bufpos, lf->len - bufpos); | | 537 | result = read(fd, lf->buf + bufpos, lf->len - bufpos); |
538 | if (result < 0) { | | 538 | if (result < 0) { |
539 | Error("%s: read error: %s", path, strerror(errno)); | | 539 | Error("%s: read error: %s", path, strerror(errno)); |
540 | exit(1); | | 540 | exit(1); |
541 | } | | 541 | } |
542 | if (result == 0) { | | 542 | if (result == 0) { |
543 | break; | | 543 | break; |
544 | } | | 544 | } |
545 | bufpos += (size_t)result; | | 545 | bufpos += (size_t)result; |
546 | } | | 546 | } |
547 | assert(bufpos <= lf->len); | | 547 | assert(bufpos <= lf->len); |
548 | lf->len = bufpos; | | 548 | lf->len = bufpos; |
549 | | | 549 | |
550 | /* truncate malloc region to actual length (maybe not useful) */ | | 550 | /* truncate malloc region to actual length (maybe not useful) */ |
551 | if (lf->len > 0) { | | 551 | if (lf->len > 0) { |
552 | /* as for mmap case, ensure trailing \n */ | | 552 | /* as for mmap case, ensure trailing \n */ |
553 | if (lf->buf[lf->len - 1] != '\n') | | 553 | if (lf->buf[lf->len - 1] != '\n') |
554 | lf->len++; | | 554 | lf->len++; |
555 | lf->buf = bmake_realloc(lf->buf, lf->len); | | 555 | lf->buf = bmake_realloc(lf->buf, lf->len); |
556 | lf->buf[lf->len - 1] = '\n'; | | 556 | lf->buf[lf->len - 1] = '\n'; |
557 | } | | 557 | } |
558 | | | 558 | |
559 | done: | | 559 | done: |
560 | if (path != NULL) { | | 560 | if (path != NULL) { |
561 | close(fd); | | 561 | close(fd); |
562 | } | | 562 | } |
563 | return lf; | | 563 | return lf; |
564 | } | | 564 | } |
565 | | | 565 | |
566 | /* old code */ | | 566 | /* old code */ |
567 | | | 567 | |
568 | /* Check if the current character is escaped on the current line. */ | | 568 | /* Check if the current character is escaped on the current line. */ |
569 | static Boolean | | 569 | static Boolean |
570 | ParseIsEscaped(const char *line, const char *c) | | 570 | ParseIsEscaped(const char *line, const char *c) |
571 | { | | 571 | { |
572 | Boolean active = FALSE; | | 572 | Boolean active = FALSE; |
573 | for (;;) { | | 573 | for (;;) { |
574 | if (line == c) | | 574 | if (line == c) |
575 | return active; | | 575 | return active; |
576 | if (*--c != '\\') | | 576 | if (*--c != '\\') |
577 | return active; | | 577 | return active; |
578 | active = !active; | | 578 | active = !active; |
579 | } | | 579 | } |
580 | } | | 580 | } |
581 | | | 581 | |
582 | /* Add the filename and lineno to the GNode so that we remember where it | | 582 | /* Add the filename and lineno to the GNode so that we remember where it |
583 | * was first defined. */ | | 583 | * was first defined. */ |
584 | static void | | 584 | static void |
585 | ParseMark(GNode *gn) | | 585 | ParseMark(GNode *gn) |
586 | { | | 586 | { |
587 | IFile *curFile = CurFile(); | | 587 | IFile *curFile = CurFile(); |
588 | gn->fname = curFile->fname; | | 588 | gn->fname = curFile->fname; |
589 | gn->lineno = curFile->lineno; | | 589 | gn->lineno = curFile->lineno; |
590 | } | | 590 | } |
591 | | | 591 | |
592 | /* Look in the table of keywords for one matching the given string. | | 592 | /* Look in the table of keywords for one matching the given string. |
593 | * Return the index of the keyword, or -1 if it isn't there. */ | | 593 | * Return the index of the keyword, or -1 if it isn't there. */ |
594 | static int | | 594 | static int |
595 | ParseFindKeyword(const char *str) | | 595 | ParseFindKeyword(const char *str) |
596 | { | | 596 | { |
597 | int start, end, cur; | | 597 | int start, end, cur; |
598 | int diff; | | 598 | int diff; |
599 | | | 599 | |
600 | start = 0; | | 600 | start = 0; |
601 | end = sizeof parseKeywords / sizeof parseKeywords[0] - 1; | | 601 | end = sizeof parseKeywords / sizeof parseKeywords[0] - 1; |
602 | | | 602 | |
603 | do { | | 603 | do { |
604 | cur = start + (end - start) / 2; | | 604 | cur = start + (end - start) / 2; |
605 | diff = strcmp(str, parseKeywords[cur].name); | | 605 | diff = strcmp(str, parseKeywords[cur].name); |
606 | | | 606 | |
607 | if (diff == 0) { | | 607 | if (diff == 0) { |
608 | return cur; | | 608 | return cur; |
609 | } else if (diff < 0) { | | 609 | } else if (diff < 0) { |
610 | end = cur - 1; | | 610 | end = cur - 1; |
611 | } else { | | 611 | } else { |
612 | start = cur + 1; | | 612 | start = cur + 1; |
613 | } | | 613 | } |
614 | } while (start <= end); | | 614 | } while (start <= end); |
615 | return -1; | | 615 | return -1; |
616 | } | | 616 | } |
617 | | | 617 | |
618 | static void | | 618 | static void |
619 | PrintLocation(FILE *f, const char *filename, size_t lineno) | | 619 | PrintLocation(FILE *f, const char *filename, size_t lineno) |
620 | { | | 620 | { |
621 | char dirbuf[MAXPATHLEN+1]; | | 621 | char dirbuf[MAXPATHLEN+1]; |
622 | const char *dir, *base; | | 622 | const char *dir, *base; |
623 | void *dir_freeIt, *base_freeIt; | | 623 | void *dir_freeIt, *base_freeIt; |
624 | | | 624 | |
625 | if (*filename == '/' || strcmp(filename, "(stdin)") == 0) { | | 625 | if (*filename == '/' || strcmp(filename, "(stdin)") == 0) { |
626 | (void)fprintf(f, "\"%s\" line %zu: ", filename, lineno); | | 626 | (void)fprintf(f, "\"%s\" line %zu: ", filename, lineno); |
627 | return; | | 627 | return; |
628 | } | | 628 | } |
629 | | | 629 | |
630 | /* Find out which makefile is the culprit. | | 630 | /* Find out which makefile is the culprit. |
631 | * We try ${.PARSEDIR} and apply realpath(3) if not absolute. */ | | 631 | * We try ${.PARSEDIR} and apply realpath(3) if not absolute. */ |
632 | | | 632 | |
633 | dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &dir_freeIt); | | 633 | dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &dir_freeIt); |
634 | if (dir == NULL) | | 634 | if (dir == NULL) |
635 | dir = "."; | | 635 | dir = "."; |
636 | if (*dir != '/') | | 636 | if (*dir != '/') |
637 | dir = realpath(dir, dirbuf); | | 637 | dir = realpath(dir, dirbuf); |
638 | | | 638 | |
639 | base = Var_Value(".PARSEFILE", VAR_GLOBAL, &base_freeIt); | | 639 | base = Var_Value(".PARSEFILE", VAR_GLOBAL, &base_freeIt); |
640 | if (base == NULL) { | | 640 | if (base == NULL) { |
641 | const char *slash = strrchr(filename, '/'); | | 641 | const char *slash = strrchr(filename, '/'); |
642 | base = slash != NULL ? slash + 1 : filename; | | 642 | base = slash != NULL ? slash + 1 : filename; |
643 | } | | 643 | } |
644 | | | 644 | |
645 | (void)fprintf(f, "\"%s/%s\" line %zu: ", dir, base, lineno); | | 645 | (void)fprintf(f, "\"%s/%s\" line %zu: ", dir, base, lineno); |
646 | bmake_free(base_freeIt); | | 646 | bmake_free(base_freeIt); |
647 | bmake_free(dir_freeIt); | | 647 | bmake_free(dir_freeIt); |
648 | } | | 648 | } |
649 | | | 649 | |
650 | /* Print a parse error message, including location information. | | 650 | /* Print a parse error message, including location information. |
651 | * | | 651 | * |
652 | * Increment "fatals" if the level is PARSE_FATAL, and continue parsing | | 652 | * Increment "fatals" if the level is PARSE_FATAL, and continue parsing |
653 | * until the end of the current top-level makefile, then exit (see | | 653 | * until the end of the current top-level makefile, then exit (see |
654 | * Parse_File). */ | | 654 | * Parse_File). */ |
655 | static void | | 655 | static void |
656 | ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, | | 656 | ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, |
657 | ParseErrorLevel type, const char *fmt, va_list ap) | | 657 | ParseErrorLevel type, const char *fmt, va_list ap) |
658 | { | | 658 | { |
659 | static Boolean fatal_warning_error_printed = FALSE; | | 659 | static Boolean fatal_warning_error_printed = FALSE; |
660 | | | 660 | |
661 | (void)fprintf(f, "%s: ", progname); | | 661 | (void)fprintf(f, "%s: ", progname); |
662 | | | 662 | |
663 | if (cfname != NULL) | | 663 | if (cfname != NULL) |
664 | PrintLocation(f, cfname, clineno); | | 664 | PrintLocation(f, cfname, clineno); |
665 | if (type == PARSE_WARNING) | | 665 | if (type == PARSE_WARNING) |
666 | (void)fprintf(f, "warning: "); | | 666 | (void)fprintf(f, "warning: "); |
667 | (void)vfprintf(f, fmt, ap); | | 667 | (void)vfprintf(f, fmt, ap); |
668 | (void)fprintf(f, "\n"); | | 668 | (void)fprintf(f, "\n"); |
669 | (void)fflush(f); | | 669 | (void)fflush(f); |
670 | | | 670 | |
671 | if (type == PARSE_INFO) | | 671 | if (type == PARSE_INFO) |
672 | return; | | 672 | return; |
673 | if (type == PARSE_FATAL || opts.parseWarnFatal) | | 673 | if (type == PARSE_FATAL || opts.parseWarnFatal) |
674 | fatals++; | | 674 | fatals++; |
675 | if (opts.parseWarnFatal && !fatal_warning_error_printed) { | | 675 | if (opts.parseWarnFatal && !fatal_warning_error_printed) { |
676 | Error("parsing warnings being treated as errors"); | | 676 | Error("parsing warnings being treated as errors"); |
677 | fatal_warning_error_printed = TRUE; | | 677 | fatal_warning_error_printed = TRUE; |
678 | } | | 678 | } |
679 | } | | 679 | } |
680 | | | 680 | |
681 | static void | | 681 | static void |
682 | ParseErrorInternal(const char *cfname, size_t clineno, ParseErrorLevel type, | | 682 | ParseErrorInternal(const char *cfname, size_t clineno, ParseErrorLevel type, |
683 | const char *fmt, ...) | | 683 | const char *fmt, ...) |
684 | { | | 684 | { |
685 | va_list ap; | | 685 | va_list ap; |
686 | | | 686 | |
687 | va_start(ap, fmt); | | 687 | va_start(ap, fmt); |
688 | (void)fflush(stdout); | | 688 | (void)fflush(stdout); |
689 | ParseVErrorInternal(stderr, cfname, clineno, type, fmt, ap); | | 689 | ParseVErrorInternal(stderr, cfname, clineno, type, fmt, ap); |
690 | va_end(ap); | | 690 | va_end(ap); |
691 | | | 691 | |
692 | if (opts.debug_file != stderr && opts.debug_file != stdout) { | | 692 | if (opts.debug_file != stderr && opts.debug_file != stdout) { |
693 | va_start(ap, fmt); | | 693 | va_start(ap, fmt); |
694 | ParseVErrorInternal(opts.debug_file, cfname, clineno, type, | | 694 | ParseVErrorInternal(opts.debug_file, cfname, clineno, type, |
695 | fmt, ap); | | 695 | fmt, ap); |
696 | va_end(ap); | | 696 | va_end(ap); |
697 | } | | 697 | } |
698 | } | | 698 | } |
699 | | | 699 | |
700 | /* External interface to ParseErrorInternal; uses the default filename and | | 700 | /* External interface to ParseErrorInternal; uses the default filename and |
701 | * line number. | | 701 | * line number. |
702 | * | | 702 | * |
703 | * Fmt is given without a trailing newline. */ | | 703 | * Fmt is given without a trailing newline. */ |
704 | void | | 704 | void |
705 | Parse_Error(ParseErrorLevel type, const char *fmt, ...) | | 705 | Parse_Error(ParseErrorLevel type, const char *fmt, ...) |
706 | { | | 706 | { |
707 | va_list ap; | | 707 | va_list ap; |
708 | const char *fname; | | 708 | const char *fname; |
709 | size_t lineno; | | 709 | size_t lineno; |
710 | | | 710 | |
711 | if (includes.len == 0) { | | 711 | if (includes.len == 0) { |
712 | fname = NULL; | | 712 | fname = NULL; |
713 | lineno = 0; | | 713 | lineno = 0; |
714 | } else { | | 714 | } else { |
715 | IFile *curFile = CurFile(); | | 715 | IFile *curFile = CurFile(); |
716 | fname = curFile->fname; | | 716 | fname = curFile->fname; |
717 | lineno = (size_t)curFile->lineno; | | 717 | lineno = (size_t)curFile->lineno; |
718 | } | | 718 | } |
719 | | | 719 | |
720 | va_start(ap, fmt); | | 720 | va_start(ap, fmt); |
721 | (void)fflush(stdout); | | 721 | (void)fflush(stdout); |
722 | ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap); | | 722 | ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap); |
723 | va_end(ap); | | 723 | va_end(ap); |
724 | | | 724 | |
725 | if (opts.debug_file != stderr && opts.debug_file != stdout) { | | 725 | if (opts.debug_file != stderr && opts.debug_file != stdout) { |
726 | va_start(ap, fmt); | | 726 | va_start(ap, fmt); |
727 | ParseVErrorInternal(opts.debug_file, fname, lineno, type, | | 727 | ParseVErrorInternal(opts.debug_file, fname, lineno, type, |
728 | fmt, ap); | | 728 | fmt, ap); |
729 | va_end(ap); | | 729 | va_end(ap); |
730 | } | | 730 | } |
731 | } | | 731 | } |
732 | | | 732 | |
733 | | | 733 | |
734 | /* Parse a .info .warning or .error directive. | | 734 | /* Parse a .info .warning or .error directive. |
735 | * | | 735 | * |
736 | * The input is the line minus the ".". We substitute variables, print the | | 736 | * The input is the line minus the ".". We substitute variables, print the |
737 | * message and exit(1) (for .error) or just print a warning if the directive | | 737 | * message and exit(1) (for .error) or just print a warning if the directive |
738 | * is malformed. | | 738 | * is malformed. |
739 | */ | | 739 | */ |
740 | static Boolean | | 740 | static Boolean |
741 | ParseMessage(const char *directive) | | 741 | ParseMessage(const char *directive) |
742 | { | | 742 | { |
743 | const char *p = directive; | | 743 | const char *p = directive; |
744 | int mtype = *p == 'i' ? PARSE_INFO : | | 744 | int mtype = *p == 'i' ? PARSE_INFO : |
745 | *p == 'w' ? PARSE_WARNING : PARSE_FATAL; | | 745 | *p == 'w' ? PARSE_WARNING : PARSE_FATAL; |
746 | char *arg; | | 746 | char *arg; |
747 | | | 747 | |
748 | while (ch_isalpha(*p)) | | 748 | while (ch_isalpha(*p)) |
749 | p++; | | 749 | p++; |
750 | if (!ch_isspace(*p)) | | 750 | if (!ch_isspace(*p)) |
751 | return FALSE; /* missing argument */ | | 751 | return FALSE; /* missing argument */ |
752 | | | 752 | |
753 | cpp_skip_whitespace(&p); | | 753 | cpp_skip_whitespace(&p); |
754 | (void)Var_Subst(p, VAR_CMDLINE, VARE_WANTRES, &arg); | | 754 | (void)Var_Subst(p, VAR_CMDLINE, VARE_WANTRES, &arg); |
755 | /* TODO: handle errors */ | | 755 | /* TODO: handle errors */ |
756 | | | 756 | |
757 | Parse_Error(mtype, "%s", arg); | | 757 | Parse_Error(mtype, "%s", arg); |
758 | free(arg); | | 758 | free(arg); |
759 | | | 759 | |
760 | if (mtype == PARSE_FATAL) { | | 760 | if (mtype == PARSE_FATAL) { |
761 | PrintOnError(NULL, NULL); | | 761 | PrintOnError(NULL, NULL); |
762 | exit(1); | | 762 | exit(1); |
763 | } | | 763 | } |
764 | return TRUE; | | 764 | return TRUE; |
765 | } | | 765 | } |
766 | | | 766 | |
767 | /* Add the child to the parent's children. | | 767 | /* Add the child to the parent's children. |
768 | * | | 768 | * |
769 | * Additionally, add the parent to the child's parents, but only if the | | 769 | * Additionally, add the parent to the child's parents, but only if the |
770 | * target is not special. An example for such a special target is .END, | | 770 | * target is not special. An example for such a special target is .END, |
771 | * which does not need to be informed once the child target has been made. */ | | 771 | * which does not need to be informed once the child target has been made. */ |
772 | static void | | 772 | static void |
773 | LinkSource(GNode *pgn, GNode *cgn, Boolean isSpecial) | | 773 | LinkSource(GNode *pgn, GNode *cgn, Boolean isSpecial) |
774 | { | | 774 | { |
775 | if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(pgn->cohorts)) | | 775 | if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(pgn->cohorts)) |
776 | pgn = pgn->cohorts->last->datum; | | 776 | pgn = pgn->cohorts->last->datum; |
777 | | | 777 | |
778 | Lst_Append(pgn->children, cgn); | | 778 | Lst_Append(pgn->children, cgn); |
779 | pgn->unmade++; | | 779 | pgn->unmade++; |
780 | | | 780 | |
781 | /* Special targets like .END don't need any children. */ | | 781 | /* Special targets like .END don't need any children. */ |
782 | if (!isSpecial) | | 782 | if (!isSpecial) |
783 | Lst_Append(cgn->parents, pgn); | | 783 | Lst_Append(cgn->parents, pgn); |
784 | | | 784 | |
785 | if (DEBUG(PARSE)) { | | 785 | if (DEBUG(PARSE)) { |
786 | debug_printf("# %s: added child %s - %s\n", | | 786 | debug_printf("# %s: added child %s - %s\n", |
787 | __func__, pgn->name, cgn->name); | | 787 | __func__, pgn->name, cgn->name); |
788 | Targ_PrintNode(pgn, 0); | | 788 | Targ_PrintNode(pgn, 0); |
789 | Targ_PrintNode(cgn, 0); | | 789 | Targ_PrintNode(cgn, 0); |
790 | } | | 790 | } |
791 | } | | 791 | } |
792 | | | 792 | |
793 | /* Add the node to each target from the current dependency group. */ | | 793 | /* Add the node to each target from the current dependency group. */ |
794 | static void | | 794 | static void |
795 | LinkToTargets(GNode *gn, Boolean isSpecial) | | 795 | LinkToTargets(GNode *gn, Boolean isSpecial) |
796 | { | | 796 | { |
797 | GNodeListNode *ln; | | 797 | GNodeListNode *ln; |
798 | for (ln = targets->first; ln != NULL; ln = ln->next) | | 798 | for (ln = targets->first; ln != NULL; ln = ln->next) |
799 | LinkSource(ln->datum, gn, isSpecial); | | 799 | LinkSource(ln->datum, gn, isSpecial); |
800 | } | | 800 | } |
801 | | | 801 | |
802 | static Boolean | | 802 | static Boolean |
803 | TryApplyDependencyOperator(GNode *gn, GNodeType op) | | 803 | TryApplyDependencyOperator(GNode *gn, GNodeType op) |
804 | { | | 804 | { |
805 | /* | | 805 | /* |
806 | * If the node occurred on the left-hand side of a dependency and the | | 806 | * If the node occurred on the left-hand side of a dependency and the |
807 | * operator also defines a dependency, they must match. | | 807 | * operator also defines a dependency, they must match. |
808 | */ | | 808 | */ |
809 | if ((op & OP_OPMASK) && (gn->type & OP_OPMASK) && | | 809 | if ((op & OP_OPMASK) && (gn->type & OP_OPMASK) && |
810 | ((op & OP_OPMASK) != (gn->type & OP_OPMASK))) | | 810 | ((op & OP_OPMASK) != (gn->type & OP_OPMASK))) |
811 | { | | 811 | { |
812 | Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name); | | 812 | Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name); |
813 | return FALSE; | | 813 | return FALSE; |
814 | } | | 814 | } |
815 | | | 815 | |
816 | if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { | | 816 | if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { |
817 | /* | | 817 | /* |
818 | * If the node was the object of a :: operator, we need to create a | | 818 | * If the node was the object of a :: operator, we need to create a |
819 | * new instance of it for the children and commands on this dependency | | 819 | * new instance of it for the children and commands on this dependency |
820 | * line. The new instance is placed on the 'cohorts' list of the | | 820 | * line. The new instance is placed on the 'cohorts' list of the |
821 | * initial one (note the initial one is not on its own cohorts list) | | 821 | * initial one (note the initial one is not on its own cohorts list) |
822 | * and the new instance is linked to all parents of the initial | | 822 | * and the new instance is linked to all parents of the initial |
823 | * instance. | | 823 | * instance. |
824 | */ | | 824 | */ |
825 | GNode *cohort; | | 825 | GNode *cohort; |
826 | | | 826 | |
827 | /* | | 827 | /* |
828 | * Propagate copied bits to the initial node. They'll be propagated | | 828 | * Propagate copied bits to the initial node. They'll be propagated |
829 | * back to the rest of the cohorts later. | | 829 | * back to the rest of the cohorts later. |
830 | */ | | 830 | */ |
831 | gn->type |= op & ~OP_OPMASK; | | 831 | gn->type |= op & ~OP_OPMASK; |
832 | | | 832 | |
833 | cohort = Targ_NewInternalNode(gn->name); | | 833 | cohort = Targ_NewInternalNode(gn->name); |
834 | if (doing_depend) | | 834 | if (doing_depend) |
835 | ParseMark(cohort); | | 835 | ParseMark(cohort); |
836 | /* | | 836 | /* |
837 | * Make the cohort invisible as well to avoid duplicating it into | | 837 | * Make the cohort invisible as well to avoid duplicating it into |
838 | * other variables. True, parents of this target won't tend to do | | 838 | * other variables. True, parents of this target won't tend to do |
839 | * anything with their local variables, but better safe than | | 839 | * anything with their local variables, but better safe than |
840 | * sorry. (I think this is pointless now, since the relevant list | | 840 | * sorry. (I think this is pointless now, since the relevant list |
841 | * traversals will no longer see this node anyway. -mycroft) | | 841 | * traversals will no longer see this node anyway. -mycroft) |
842 | */ | | 842 | */ |
843 | cohort->type = op | OP_INVISIBLE; | | 843 | cohort->type = op | OP_INVISIBLE; |
844 | Lst_Append(gn->cohorts, cohort); | | 844 | Lst_Append(gn->cohorts, cohort); |
845 | cohort->centurion = gn; | | 845 | cohort->centurion = gn; |
846 | gn->unmade_cohorts++; | | 846 | gn->unmade_cohorts++; |
847 | snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", | | 847 | snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", |
848 | (unsigned int)gn->unmade_cohorts % 1000000); | | 848 | (unsigned int)gn->unmade_cohorts % 1000000); |
849 | } else { | | 849 | } else { |
850 | /* | | 850 | /* |
851 | * We don't want to nuke any previous flags (whatever they were) so we | | 851 | * We don't want to nuke any previous flags (whatever they were) so we |
852 | * just OR the new operator into the old | | 852 | * just OR the new operator into the old |
853 | */ | | 853 | */ |
854 | gn->type |= op; | | 854 | gn->type |= op; |
855 | } | | 855 | } |
856 | | | 856 | |
857 | return TRUE; | | 857 | return TRUE; |
858 | } | | 858 | } |
859 | | | 859 | |
860 | static void | | 860 | static void |
861 | ApplyDependencyOperator(GNodeType op) | | 861 | ApplyDependencyOperator(GNodeType op) |
862 | { | | 862 | { |
863 | GNodeListNode *ln; | | 863 | GNodeListNode *ln; |
864 | for (ln = targets->first; ln != NULL; ln = ln->next) | | 864 | for (ln = targets->first; ln != NULL; ln = ln->next) |
865 | if (!TryApplyDependencyOperator(ln->datum, op)) | | 865 | if (!TryApplyDependencyOperator(ln->datum, op)) |
866 | break; | | 866 | break; |
867 | } | | 867 | } |
868 | | | 868 | |
869 | static Boolean | | 869 | static Boolean |
870 | ParseDoSrcKeyword(const char *src, ParseSpecial specType) | | 870 | ParseDoSrcKeyword(const char *src, ParseSpecial specType) |
871 | { | | 871 | { |
872 | static int wait_number = 0; | | 872 | static int wait_number = 0; |
873 | char wait_src[16]; | | 873 | char wait_src[16]; |
874 | GNode *gn; | | 874 | GNode *gn; |
875 | | | 875 | |
876 | if (*src == '.' && ch_isupper(src[1])) { | | 876 | if (*src == '.' && ch_isupper(src[1])) { |
877 | int keywd = ParseFindKeyword(src); | | 877 | int keywd = ParseFindKeyword(src); |
878 | if (keywd != -1) { | | 878 | if (keywd != -1) { |
879 | int op = parseKeywords[keywd].op; | | 879 | int op = parseKeywords[keywd].op; |
880 | if (op != 0) { | | 880 | if (op != 0) { |
881 | ApplyDependencyOperator(op); | | 881 | ApplyDependencyOperator(op); |
882 | return TRUE; | | 882 | return TRUE; |
883 | } | | 883 | } |
884 | if (parseKeywords[keywd].spec == SP_WAIT) { | | 884 | if (parseKeywords[keywd].spec == SP_WAIT) { |
885 | /* | | 885 | /* |
886 | * We add a .WAIT node in the dependency list. | | 886 | * We add a .WAIT node in the dependency list. |
887 | * After any dynamic dependencies (and filename globbing) | | 887 | * After any dynamic dependencies (and filename globbing) |
888 | * have happened, it is given a dependency on the each | | 888 | * have happened, it is given a dependency on the each |
889 | * previous child back to and previous .WAIT node. | | 889 | * previous child back to and previous .WAIT node. |
890 | * The next child won't be scheduled until the .WAIT node | | 890 | * The next child won't be scheduled until the .WAIT node |
891 | * is built. | | 891 | * is built. |
892 | * We give each .WAIT node a unique name (mainly for diag). | | 892 | * We give each .WAIT node a unique name (mainly for diag). |
893 | */ | | 893 | */ |
894 | snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number); | | 894 | snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number); |
895 | gn = Targ_NewInternalNode(wait_src); | | 895 | gn = Targ_NewInternalNode(wait_src); |
896 | if (doing_depend) | | 896 | if (doing_depend) |
897 | ParseMark(gn); | | 897 | ParseMark(gn); |
898 | gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; | | 898 | gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; |
899 | LinkToTargets(gn, specType != SP_NOT); | | 899 | LinkToTargets(gn, specType != SP_NOT); |
900 | return TRUE; | | 900 | return TRUE; |
901 | } | | 901 | } |
902 | } | | 902 | } |
903 | } | | 903 | } |
904 | return FALSE; | | 904 | return FALSE; |
905 | } | | 905 | } |
906 | | | 906 | |
907 | static void | | 907 | static void |
908 | ParseDoSrcMain(const char *src) | | 908 | ParseDoSrcMain(const char *src) |
909 | { | | 909 | { |
910 | /* | | 910 | /* |
911 | * If we have noted the existence of a .MAIN, it means we need | | 911 | * If we have noted the existence of a .MAIN, it means we need |
912 | * to add the sources of said target to the list of things | | 912 | * to add the sources of said target to the list of things |
913 | * to create. The string 'src' is likely to be free, so we | | 913 | * to create. The string 'src' is likely to be free, so we |
914 | * must make a new copy of it. Note that this will only be | | 914 | * must make a new copy of it. Note that this will only be |
915 | * invoked if the user didn't specify a target on the command | | 915 | * invoked if the user didn't specify a target on the command |
916 | * line. This is to allow #ifmake's to succeed, or something... | | 916 | * line. This is to allow #ifmake's to succeed, or something... |
917 | */ | | 917 | */ |
918 | Lst_Append(opts.create, bmake_strdup(src)); | | 918 | Lst_Append(opts.create, bmake_strdup(src)); |
919 | /* | | 919 | /* |
920 | * Add the name to the .TARGETS variable as well, so the user can | | 920 | * Add the name to the .TARGETS variable as well, so the user can |
921 | * employ that, if desired. | | 921 | * employ that, if desired. |
922 | */ | | 922 | */ |
923 | Var_Append(".TARGETS", src, VAR_GLOBAL); | | 923 | Var_Append(".TARGETS", src, VAR_GLOBAL); |
924 | } | | 924 | } |
925 | | | 925 | |
926 | static void | | 926 | static void |
927 | ParseDoSrcOrder(const char *src) | | 927 | ParseDoSrcOrder(const char *src) |
928 | { | | 928 | { |
929 | GNode *gn; | | 929 | GNode *gn; |
930 | /* | | 930 | /* |
931 | * Create proper predecessor/successor links between the previous | | 931 | * Create proper predecessor/successor links between the previous |
932 | * source and the current one. | | 932 | * source and the current one. |
933 | */ | | 933 | */ |
934 | gn = Targ_GetNode(src); | | 934 | gn = Targ_GetNode(src); |
935 | if (doing_depend) | | 935 | if (doing_depend) |
936 | ParseMark(gn); | | 936 | ParseMark(gn); |
937 | if (order_pred != NULL) { | | 937 | if (order_pred != NULL) { |
938 | Lst_Append(order_pred->order_succ, gn); | | 938 | Lst_Append(order_pred->order_succ, gn); |
939 | Lst_Append(gn->order_pred, order_pred); | | 939 | Lst_Append(gn->order_pred, order_pred); |
940 | if (DEBUG(PARSE)) { | | 940 | if (DEBUG(PARSE)) { |
941 | debug_printf("# %s: added Order dependency %s - %s\n", | | 941 | debug_printf("# %s: added Order dependency %s - %s\n", |
942 | __func__, order_pred->name, gn->name); | | 942 | __func__, order_pred->name, gn->name); |
943 | Targ_PrintNode(order_pred, 0); | | 943 | Targ_PrintNode(order_pred, 0); |
944 | Targ_PrintNode(gn, 0); | | 944 | Targ_PrintNode(gn, 0); |
945 | } | | 945 | } |
946 | } | | 946 | } |
947 | /* | | 947 | /* |
948 | * The current source now becomes the predecessor for the next one. | | 948 | * The current source now becomes the predecessor for the next one. |
949 | */ | | 949 | */ |
950 | order_pred = gn; | | 950 | order_pred = gn; |
951 | } | | 951 | } |
952 | | | 952 | |
953 | static void | | 953 | static void |
954 | ParseDoSrcOther(const char *src, GNodeType tOp, ParseSpecial specType) | | 954 | ParseDoSrcOther(const char *src, GNodeType tOp, ParseSpecial specType) |
955 | { | | 955 | { |
956 | GNode *gn; | | 956 | GNode *gn; |
957 | | | 957 | |
958 | /* | | 958 | /* |
959 | * If the source is not an attribute, we need to find/create | | 959 | * If the source is not an attribute, we need to find/create |
960 | * a node for it. After that we can apply any operator to it | | 960 | * a node for it. After that we can apply any operator to it |
961 | * from a special target or link it to its parents, as | | 961 | * from a special target or link it to its parents, as |
962 | * appropriate. | | 962 | * appropriate. |
963 | * | | 963 | * |
964 | * In the case of a source that was the object of a :: operator, | | 964 | * In the case of a source that was the object of a :: operator, |
965 | * the attribute is applied to all of its instances (as kept in | | 965 | * the attribute is applied to all of its instances (as kept in |
966 | * the 'cohorts' list of the node) or all the cohorts are linked | | 966 | * the 'cohorts' list of the node) or all the cohorts are linked |
967 | * to all the targets. | | 967 | * to all the targets. |
968 | */ | | 968 | */ |
969 | | | 969 | |
970 | /* Find/create the 'src' node and attach to all targets */ | | 970 | /* Find/create the 'src' node and attach to all targets */ |
971 | gn = Targ_GetNode(src); | | 971 | gn = Targ_GetNode(src); |
972 | if (doing_depend) | | 972 | if (doing_depend) |
973 | ParseMark(gn); | | 973 | ParseMark(gn); |
974 | if (tOp) { | | 974 | if (tOp) { |
975 | gn->type |= tOp; | | 975 | gn->type |= tOp; |
976 | } else { | | 976 | } else { |
977 | LinkToTargets(gn, specType != SP_NOT); | | 977 | LinkToTargets(gn, specType != SP_NOT); |
978 | } | | 978 | } |
979 | } | | 979 | } |
980 | | | 980 | |
981 | /* Given the name of a source in a dependency line, figure out if it is an | | 981 | /* Given the name of a source in a dependency line, figure out if it is an |
982 | * attribute (such as .SILENT) and apply it to the targets if it is. Else | | 982 | * attribute (such as .SILENT) and apply it to the targets if it is. Else |
983 | * decide if there is some attribute which should be applied *to* the source | | 983 | * decide if there is some attribute which should be applied *to* the source |
984 | * because of some special target (such as .PHONY) and apply it if so. | | 984 | * because of some special target (such as .PHONY) and apply it if so. |
985 | * Otherwise, make the source a child of the targets in the list 'targets'. | | 985 | * Otherwise, make the source a child of the targets in the list 'targets'. |
986 | * | | 986 | * |
987 | * Input: | | 987 | * Input: |
988 | * tOp operator (if any) from special targets | | 988 | * tOp operator (if any) from special targets |
989 | * src name of the source to handle | | 989 | * src name of the source to handle |
990 | */ | | 990 | */ |
991 | static void | | 991 | static void |
992 | ParseDoSrc(GNodeType tOp, const char *src, ParseSpecial specType) | | 992 | ParseDoSrc(GNodeType tOp, const char *src, ParseSpecial specType) |
993 | { | | 993 | { |
994 | if (ParseDoSrcKeyword(src, specType)) | | 994 | if (ParseDoSrcKeyword(src, specType)) |
995 | return; | | 995 | return; |
996 | | | 996 | |
997 | if (specType == SP_MAIN) | | 997 | if (specType == SP_MAIN) |
998 | ParseDoSrcMain(src); | | 998 | ParseDoSrcMain(src); |
999 | else if (specType == SP_ORDER) | | 999 | else if (specType == SP_ORDER) |
1000 | ParseDoSrcOrder(src); | | 1000 | ParseDoSrcOrder(src); |
1001 | else | | 1001 | else |
1002 | ParseDoSrcOther(src, tOp, specType); | | 1002 | ParseDoSrcOther(src, tOp, specType); |
1003 | } | | 1003 | } |
1004 | | | 1004 | |
1005 | /* If we have yet to decide on a main target to make, in the absence of any | | 1005 | /* If we have yet to decide on a main target to make, in the absence of any |
1006 | * user input, we want the first target on the first dependency line that is | | 1006 | * user input, we want the first target on the first dependency line that is |
1007 | * actually a real target (i.e. isn't a .USE or .EXEC rule) to be made. */ | | 1007 | * actually a real target (i.e. isn't a .USE or .EXEC rule) to be made. */ |
1008 | static void | | 1008 | static void |
1009 | FindMainTarget(void) | | 1009 | FindMainTarget(void) |
1010 | { | | 1010 | { |
1011 | GNodeListNode *ln; | | 1011 | GNodeListNode *ln; |
1012 | | | 1012 | |
1013 | if (mainNode != NULL) | | 1013 | if (mainNode != NULL) |
1014 | return; | | 1014 | return; |
1015 | | | 1015 | |
1016 | for (ln = targets->first; ln != NULL; ln = ln->next) { | | 1016 | for (ln = targets->first; ln != NULL; ln = ln->next) { |
1017 | GNode *gn = ln->datum; | | 1017 | GNode *gn = ln->datum; |
1018 | if (!(gn->type & OP_NOTARGET)) { | | 1018 | if (!(gn->type & OP_NOTARGET)) { |
1019 | mainNode = gn; | | 1019 | mainNode = gn; |
1020 | Targ_SetMain(gn); | | 1020 | Targ_SetMain(gn); |
1021 | return; | | 1021 | return; |
1022 | } | | 1022 | } |
1023 | } | | 1023 | } |
1024 | } | | 1024 | } |
1025 | | | 1025 | |
1026 | /* | | 1026 | /* |
1027 | * We got to the end of the line while we were still looking at targets. | | 1027 | * We got to the end of the line while we were still looking at targets. |
1028 | * | | 1028 | * |
1029 | * Ending a dependency line without an operator is a Bozo no-no. As a | | 1029 | * Ending a dependency line without an operator is a Bozo no-no. As a |
1030 | * heuristic, this is also often triggered by undetected conflicts from | | 1030 | * heuristic, this is also often triggered by undetected conflicts from |
1031 | * cvs/rcs merges. | | 1031 | * cvs/rcs merges. |
1032 | */ | | 1032 | */ |
1033 | static void | | 1033 | static void |
1034 | ParseErrorNoDependency(const char *lstart) | | 1034 | ParseErrorNoDependency(const char *lstart) |
1035 | { | | 1035 | { |
1036 | if ((strncmp(lstart, "<<<<<<", 6) == 0) || | | 1036 | if ((strncmp(lstart, "<<<<<<", 6) == 0) || |
1037 | (strncmp(lstart, "======", 6) == 0) || | | 1037 | (strncmp(lstart, "======", 6) == 0) || |
1038 | (strncmp(lstart, ">>>>>>", 6) == 0)) | | 1038 | (strncmp(lstart, ">>>>>>", 6) == 0)) |
1039 | Parse_Error(PARSE_FATAL, | | 1039 | Parse_Error(PARSE_FATAL, |
1040 | "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts"); | | 1040 | "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts"); |
1041 | else if (lstart[0] == '.') { | | 1041 | else if (lstart[0] == '.') { |
1042 | const char *dirstart = lstart + 1; | | 1042 | const char *dirstart = lstart + 1; |
1043 | const char *dirend; | | 1043 | const char *dirend; |
1044 | cpp_skip_whitespace(&dirstart); | | 1044 | cpp_skip_whitespace(&dirstart); |
1045 | dirend = dirstart; | | 1045 | dirend = dirstart; |
1046 | while (ch_isalnum(*dirend) || *dirend == '-') | | 1046 | while (ch_isalnum(*dirend) || *dirend == '-') |
1047 | dirend++; | | 1047 | dirend++; |
1048 | Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", | | 1048 | Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", |
1049 | (int)(dirend - dirstart), dirstart); | | 1049 | (int)(dirend - dirstart), dirstart); |
1050 | } else | | 1050 | } else |
1051 | Parse_Error(PARSE_FATAL, "Need an operator"); | | 1051 | Parse_Error(PARSE_FATAL, "Need an operator"); |
1052 | } | | 1052 | } |
1053 | | | 1053 | |
1054 | static void | | 1054 | static void |
1055 | ParseDependencyTargetWord(/*const*/ char **pp, const char *lstart) | | 1055 | ParseDependencyTargetWord(/*const*/ char **pp, const char *lstart) |
1056 | { | | 1056 | { |
1057 | /*const*/ char *cp = *pp; | | 1057 | /*const*/ char *cp = *pp; |
1058 | | | 1058 | |
1059 | while (*cp != '\0') { | | 1059 | while (*cp != '\0') { |
1060 | if ((ch_isspace(*cp) || *cp == '!' || *cp == ':' || *cp == '(') && | | 1060 | if ((ch_isspace(*cp) || *cp == '!' || *cp == ':' || *cp == '(') && |
1061 | !ParseIsEscaped(lstart, cp)) | | 1061 | !ParseIsEscaped(lstart, cp)) |
1062 | break; | | 1062 | break; |
1063 | | | 1063 | |
1064 | if (*cp == '$') { | | 1064 | if (*cp == '$') { |
1065 | /* | | 1065 | /* |
1066 | * Must be a dynamic source (would have been expanded | | 1066 | * Must be a dynamic source (would have been expanded |
1067 | * otherwise), so call the Var module to parse the puppy | | 1067 | * otherwise), so call the Var module to parse the puppy |
1068 | * so we can safely advance beyond it...There should be | | 1068 | * so we can safely advance beyond it...There should be |
1069 | * no errors in this, as they would have been discovered | | 1069 | * no errors in this, as they would have been discovered |
1070 | * in the initial Var_Subst and we wouldn't be here. | | 1070 | * in the initial Var_Subst and we wouldn't be here. |
1071 | */ | | 1071 | */ |
1072 | const char *nested_p = cp; | | 1072 | const char *nested_p = cp; |
1073 | const char *nested_val; | | 1073 | const char *nested_val; |
1074 | void *freeIt; | | 1074 | void *freeIt; |
1075 | | | 1075 | |
1076 | (void)Var_Parse(&nested_p, VAR_CMDLINE, VARE_UNDEFERR|VARE_WANTRES, | | 1076 | (void)Var_Parse(&nested_p, VAR_CMDLINE, VARE_UNDEFERR|VARE_WANTRES, |
1077 | &nested_val, &freeIt); | | 1077 | &nested_val, &freeIt); |
1078 | /* TODO: handle errors */ | | 1078 | /* TODO: handle errors */ |
1079 | free(freeIt); | | 1079 | free(freeIt); |
1080 | cp += nested_p - cp; | | 1080 | cp += nested_p - cp; |
1081 | } else | | 1081 | } else |
1082 | cp++; | | 1082 | cp++; |
1083 | } | | 1083 | } |
1084 | | | 1084 | |
1085 | *pp = cp; | | 1085 | *pp = cp; |
1086 | } | | 1086 | } |
1087 | | | 1087 | |
1088 | /* | | 1088 | /* |
1089 | * Certain special targets have special semantics: | | 1089 | * Certain special targets have special semantics: |
1090 | * .PATH Have to set the dirSearchPath | | 1090 | * .PATH Have to set the dirSearchPath |
1091 | * variable too | | 1091 | * variable too |
1092 | * .MAIN Its sources are only used if | | 1092 | * .MAIN Its sources are only used if |
1093 | * nothing has been specified to | | 1093 | * nothing has been specified to |
1094 | * create. | | 1094 | * create. |
1095 | * .DEFAULT Need to create a node to hang | | 1095 | * .DEFAULT Need to create a node to hang |
1096 | * commands on, but we don't want | | 1096 | * commands on, but we don't want |
1097 | * it in the graph, nor do we want | | 1097 | * it in the graph, nor do we want |
1098 | * it to be the Main Target, so we | | 1098 | * it to be the Main Target, so we |
1099 | * create it, set OP_NOTMAIN and | | 1099 | * create it, set OP_NOTMAIN and |
1100 | * add it to the list, setting | | 1100 | * add it to the list, setting |
1101 | * DEFAULT to the new node for | | 1101 | * DEFAULT to the new node for |
1102 | * later use. We claim the node is | | 1102 | * later use. We claim the node is |
1103 | * A transformation rule to make | | 1103 | * A transformation rule to make |
1104 | * life easier later, when we'll | | 1104 | * life easier later, when we'll |
1105 | * use Make_HandleUse to actually | | 1105 | * use Make_HandleUse to actually |
1106 | * apply the .DEFAULT commands. | | 1106 | * apply the .DEFAULT commands. |
1107 | * .PHONY The list of targets | | 1107 | * .PHONY The list of targets |
1108 | * .NOPATH Don't search for file in the path | | 1108 | * .NOPATH Don't search for file in the path |
1109 | * .STALE | | 1109 | * .STALE |
1110 | * .BEGIN | | 1110 | * .BEGIN |
1111 | * .END | | 1111 | * .END |
1112 | * .ERROR | | 1112 | * .ERROR |
1113 | * .DELETE_ON_ERROR | | 1113 | * .DELETE_ON_ERROR |
1114 | * .INTERRUPT Are not to be considered the | | 1114 | * .INTERRUPT Are not to be considered the |
1115 | * main target. | | 1115 | * main target. |
1116 | * .NOTPARALLEL Make only one target at a time. | | 1116 | * .NOTPARALLEL Make only one target at a time. |
1117 | * .SINGLESHELL Create a shell for each command. | | 1117 | * .SINGLESHELL Create a shell for each command. |
1118 | * .ORDER Must set initial predecessor to NULL | | 1118 | * .ORDER Must set initial predecessor to NULL |
1119 | */ | | 1119 | */ |
| @@ -1370,1836 +1370,1836 @@ ParseDoDependencySourcesEmpty(ParseSpeci | | | @@ -1370,1836 +1370,1836 @@ ParseDoDependencySourcesEmpty(ParseSpeci |
1370 | break; | | 1370 | break; |
1371 | case SP_SILENT: | | 1371 | case SP_SILENT: |
1372 | opts.beSilent = TRUE; | | 1372 | opts.beSilent = TRUE; |
1373 | break; | | 1373 | break; |
1374 | case SP_PATH: | | 1374 | case SP_PATH: |
1375 | ClearPaths(paths); | | 1375 | ClearPaths(paths); |
1376 | break; | | 1376 | break; |
1377 | #ifdef POSIX | | 1377 | #ifdef POSIX |
1378 | case SP_POSIX: | | 1378 | case SP_POSIX: |
1379 | Var_Set("%POSIX", "1003.2", VAR_GLOBAL); | | 1379 | Var_Set("%POSIX", "1003.2", VAR_GLOBAL); |
1380 | break; | | 1380 | break; |
1381 | #endif | | 1381 | #endif |
1382 | default: | | 1382 | default: |
1383 | break; | | 1383 | break; |
1384 | } | | 1384 | } |
1385 | } | | 1385 | } |
1386 | | | 1386 | |
1387 | static void | | 1387 | static void |
1388 | AddToPaths(const char *dir, SearchPathList *paths) | | 1388 | AddToPaths(const char *dir, SearchPathList *paths) |
1389 | { | | 1389 | { |
1390 | if (paths != NULL) { | | 1390 | if (paths != NULL) { |
1391 | SearchPathListNode *ln; | | 1391 | SearchPathListNode *ln; |
1392 | for (ln = paths->first; ln != NULL; ln = ln->next) | | 1392 | for (ln = paths->first; ln != NULL; ln = ln->next) |
1393 | (void)Dir_AddDir(ln->datum, dir); | | 1393 | (void)Dir_AddDir(ln->datum, dir); |
1394 | } | | 1394 | } |
1395 | } | | 1395 | } |
1396 | | | 1396 | |
1397 | /* | | 1397 | /* |
1398 | * If the target was one that doesn't take files as its sources | | 1398 | * If the target was one that doesn't take files as its sources |
1399 | * but takes something like suffixes, we take each | | 1399 | * but takes something like suffixes, we take each |
1400 | * space-separated word on the line as a something and deal | | 1400 | * space-separated word on the line as a something and deal |
1401 | * with it accordingly. | | 1401 | * with it accordingly. |
1402 | * | | 1402 | * |
1403 | * If the target was .SUFFIXES, we take each source as a | | 1403 | * If the target was .SUFFIXES, we take each source as a |
1404 | * suffix and add it to the list of suffixes maintained by the | | 1404 | * suffix and add it to the list of suffixes maintained by the |
1405 | * Suff module. | | 1405 | * Suff module. |
1406 | * | | 1406 | * |
1407 | * If the target was a .PATH, we add the source as a directory | | 1407 | * If the target was a .PATH, we add the source as a directory |
1408 | * to search on the search path. | | 1408 | * to search on the search path. |
1409 | * | | 1409 | * |
1410 | * If it was .INCLUDES, the source is taken to be the suffix of | | 1410 | * If it was .INCLUDES, the source is taken to be the suffix of |
1411 | * files which will be #included and whose search path should | | 1411 | * files which will be #included and whose search path should |
1412 | * be present in the .INCLUDES variable. | | 1412 | * be present in the .INCLUDES variable. |
1413 | * | | 1413 | * |
1414 | * If it was .LIBS, the source is taken to be the suffix of | | 1414 | * If it was .LIBS, the source is taken to be the suffix of |
1415 | * files which are considered libraries and whose search path | | 1415 | * files which are considered libraries and whose search path |
1416 | * should be present in the .LIBS variable. | | 1416 | * should be present in the .LIBS variable. |
1417 | * | | 1417 | * |
1418 | * If it was .NULL, the source is the suffix to use when a file | | 1418 | * If it was .NULL, the source is the suffix to use when a file |
1419 | * has no valid suffix. | | 1419 | * has no valid suffix. |
1420 | * | | 1420 | * |
1421 | * If it was .OBJDIR, the source is a new definition for .OBJDIR, | | 1421 | * If it was .OBJDIR, the source is a new definition for .OBJDIR, |
1422 | * and will cause make to do a new chdir to that path. | | 1422 | * and will cause make to do a new chdir to that path. |
1423 | */ | | 1423 | */ |
1424 | static void | | 1424 | static void |
1425 | ParseDoDependencySourceSpecial(ParseSpecial specType, char *word, | | 1425 | ParseDoDependencySourceSpecial(ParseSpecial specType, char *word, |
1426 | SearchPathList *paths) | | 1426 | SearchPathList *paths) |
1427 | { | | 1427 | { |
1428 | switch (specType) { | | 1428 | switch (specType) { |
1429 | case SP_SUFFIXES: | | 1429 | case SP_SUFFIXES: |
1430 | Suff_AddSuffix(word, &mainNode); | | 1430 | Suff_AddSuffix(word, &mainNode); |
1431 | break; | | 1431 | break; |
1432 | case SP_PATH: | | 1432 | case SP_PATH: |
1433 | AddToPaths(word, paths); | | 1433 | AddToPaths(word, paths); |
1434 | break; | | 1434 | break; |
1435 | case SP_INCLUDES: | | 1435 | case SP_INCLUDES: |
1436 | Suff_AddInclude(word); | | 1436 | Suff_AddInclude(word); |
1437 | break; | | 1437 | break; |
1438 | case SP_LIBS: | | 1438 | case SP_LIBS: |
1439 | Suff_AddLib(word); | | 1439 | Suff_AddLib(word); |
1440 | break; | | 1440 | break; |
1441 | case SP_NULL: | | 1441 | case SP_NULL: |
1442 | Suff_SetNull(word); | | 1442 | Suff_SetNull(word); |
1443 | break; | | 1443 | break; |
1444 | case SP_OBJDIR: | | 1444 | case SP_OBJDIR: |
1445 | Main_SetObjdir("%s", word); | | 1445 | Main_SetObjdir("%s", word); |
1446 | break; | | 1446 | break; |
1447 | default: | | 1447 | default: |
1448 | break; | | 1448 | break; |
1449 | } | | 1449 | } |
1450 | } | | 1450 | } |
1451 | | | 1451 | |
1452 | static Boolean | | 1452 | static Boolean |
1453 | ParseDoDependencyTargets(char **inout_cp, | | 1453 | ParseDoDependencyTargets(char **inout_cp, |
1454 | char **inout_line, | | 1454 | char **inout_line, |
1455 | const char *lstart, | | 1455 | const char *lstart, |
1456 | ParseSpecial *inout_specType, | | 1456 | ParseSpecial *inout_specType, |
1457 | GNodeType *inout_tOp, | | 1457 | GNodeType *inout_tOp, |
1458 | SearchPathList **inout_paths, | | 1458 | SearchPathList **inout_paths, |
1459 | StringList *curTargs) | | 1459 | StringList *curTargs) |
1460 | { | | 1460 | { |
1461 | char *cp = *inout_cp; | | 1461 | char *cp = *inout_cp; |
1462 | char *line = *inout_line; | | 1462 | char *line = *inout_line; |
1463 | char savec; | | 1463 | char savec; |
1464 | | | 1464 | |
1465 | for (;;) { | | 1465 | for (;;) { |
1466 | /* | | 1466 | /* |
1467 | * Here LINE points to the beginning of the next word, and | | 1467 | * Here LINE points to the beginning of the next word, and |
1468 | * LSTART points to the actual beginning of the line. | | 1468 | * LSTART points to the actual beginning of the line. |
1469 | */ | | 1469 | */ |
1470 | | | 1470 | |
1471 | /* Find the end of the next word. */ | | 1471 | /* Find the end of the next word. */ |
1472 | cp = line; | | 1472 | cp = line; |
1473 | ParseDependencyTargetWord(&cp, lstart); | | 1473 | ParseDependencyTargetWord(&cp, lstart); |
1474 | | | 1474 | |
1475 | /* | | 1475 | /* |
1476 | * If the word is followed by a left parenthesis, it's the | | 1476 | * If the word is followed by a left parenthesis, it's the |
1477 | * name of an object file inside an archive (ar file). | | 1477 | * name of an object file inside an archive (ar file). |
1478 | */ | | 1478 | */ |
1479 | if (!ParseIsEscaped(lstart, cp) && *cp == '(') { | | 1479 | if (!ParseIsEscaped(lstart, cp) && *cp == '(') { |
1480 | /* | | 1480 | /* |
1481 | * Archives must be handled specially to make sure the OP_ARCHV | | 1481 | * Archives must be handled specially to make sure the OP_ARCHV |
1482 | * flag is set in their 'type' field, for one thing, and because | | 1482 | * flag is set in their 'type' field, for one thing, and because |
1483 | * things like "archive(file1.o file2.o file3.o)" are permissible. | | 1483 | * things like "archive(file1.o file2.o file3.o)" are permissible. |
1484 | * Arch_ParseArchive will set 'line' to be the first non-blank | | 1484 | * Arch_ParseArchive will set 'line' to be the first non-blank |
1485 | * after the archive-spec. It creates/finds nodes for the members | | 1485 | * after the archive-spec. It creates/finds nodes for the members |
1486 | * and places them on the given list, returning TRUE if all | | 1486 | * and places them on the given list, returning TRUE if all |
1487 | * went well and FALSE if there was an error in the | | 1487 | * went well and FALSE if there was an error in the |
1488 | * specification. On error, line should remain untouched. | | 1488 | * specification. On error, line should remain untouched. |
1489 | */ | | 1489 | */ |
1490 | if (!Arch_ParseArchive(&line, targets, VAR_CMDLINE)) { | | 1490 | if (!Arch_ParseArchive(&line, targets, VAR_CMDLINE)) { |
1491 | Parse_Error(PARSE_FATAL, | | 1491 | Parse_Error(PARSE_FATAL, |
1492 | "Error in archive specification: \"%s\"", line); | | 1492 | "Error in archive specification: \"%s\"", line); |
1493 | return FALSE; | | 1493 | return FALSE; |
1494 | } else { | | 1494 | } else { |
1495 | /* Done with this word; on to the next. */ | | 1495 | /* Done with this word; on to the next. */ |
1496 | cp = line; | | 1496 | cp = line; |
1497 | continue; | | 1497 | continue; |
1498 | } | | 1498 | } |
1499 | } | | 1499 | } |
1500 | | | 1500 | |
1501 | if (!*cp) { | | 1501 | if (!*cp) { |
1502 | ParseErrorNoDependency(lstart); | | 1502 | ParseErrorNoDependency(lstart); |
1503 | return FALSE; | | 1503 | return FALSE; |
1504 | } | | 1504 | } |
1505 | | | 1505 | |
1506 | /* Insert a null terminator. */ | | 1506 | /* Insert a null terminator. */ |
1507 | savec = *cp; | | 1507 | savec = *cp; |
1508 | *cp = '\0'; | | 1508 | *cp = '\0'; |
1509 | | | 1509 | |
1510 | if (!ParseDoDependencyTarget(line, inout_specType, inout_tOp, | | 1510 | if (!ParseDoDependencyTarget(line, inout_specType, inout_tOp, |
1511 | inout_paths)) | | 1511 | inout_paths)) |
1512 | return FALSE; | | 1512 | return FALSE; |
1513 | | | 1513 | |
1514 | /* | | 1514 | /* |
1515 | * Have word in line. Get or create its node and stick it at | | 1515 | * Have word in line. Get or create its node and stick it at |
1516 | * the end of the targets list | | 1516 | * the end of the targets list |
1517 | */ | | 1517 | */ |
1518 | if (*inout_specType == SP_NOT && *line != '\0') { | | 1518 | if (*inout_specType == SP_NOT && *line != '\0') { |
1519 | ParseDoDependencyTargetMundane(line, curTargs); | | 1519 | ParseDoDependencyTargetMundane(line, curTargs); |
1520 | } else if (*inout_specType == SP_PATH && *line != '.' && *line != '\0') { | | 1520 | } else if (*inout_specType == SP_PATH && *line != '.' && *line != '\0') { |
1521 | Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); | | 1521 | Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); |
1522 | } | | 1522 | } |
1523 | | | 1523 | |
1524 | /* Don't need the inserted null terminator any more. */ | | 1524 | /* Don't need the inserted null terminator any more. */ |
1525 | *cp = savec; | | 1525 | *cp = savec; |
1526 | | | 1526 | |
1527 | /* | | 1527 | /* |
1528 | * If it is a special type and not .PATH, it's the only target we | | 1528 | * If it is a special type and not .PATH, it's the only target we |
1529 | * allow on this line... | | 1529 | * allow on this line... |
1530 | */ | | 1530 | */ |
1531 | if (*inout_specType != SP_NOT && *inout_specType != SP_PATH) { | | 1531 | if (*inout_specType != SP_NOT && *inout_specType != SP_PATH) { |
1532 | ParseDoDependencyTargetExtraWarn(&cp, lstart); | | 1532 | ParseDoDependencyTargetExtraWarn(&cp, lstart); |
1533 | } else { | | 1533 | } else { |
1534 | pp_skip_whitespace(&cp); | | 1534 | pp_skip_whitespace(&cp); |
1535 | } | | 1535 | } |
1536 | line = cp; | | 1536 | line = cp; |
1537 | if (*line == '\0') | | 1537 | if (*line == '\0') |
1538 | break; | | 1538 | break; |
1539 | if ((*line == '!' || *line == ':') && !ParseIsEscaped(lstart, line)) | | 1539 | if ((*line == '!' || *line == ':') && !ParseIsEscaped(lstart, line)) |
1540 | break; | | 1540 | break; |
1541 | } | | 1541 | } |
1542 | | | 1542 | |
1543 | *inout_cp = cp; | | 1543 | *inout_cp = cp; |
1544 | *inout_line = line; | | 1544 | *inout_line = line; |
1545 | return TRUE; | | 1545 | return TRUE; |
1546 | } | | 1546 | } |
1547 | | | 1547 | |
1548 | static void | | 1548 | static void |
1549 | ParseDoDependencySourcesSpecial(char *start, char *end, | | 1549 | ParseDoDependencySourcesSpecial(char *start, char *end, |
1550 | ParseSpecial specType, SearchPathList *paths) | | 1550 | ParseSpecial specType, SearchPathList *paths) |
1551 | { | | 1551 | { |
1552 | char savec; | | 1552 | char savec; |
1553 | | | 1553 | |
1554 | while (*start) { | | 1554 | while (*start) { |
1555 | while (*end && !ch_isspace(*end)) | | 1555 | while (*end && !ch_isspace(*end)) |
1556 | end++; | | 1556 | end++; |
1557 | savec = *end; | | 1557 | savec = *end; |
1558 | *end = '\0'; | | 1558 | *end = '\0'; |
1559 | ParseDoDependencySourceSpecial(specType, start, paths); | | 1559 | ParseDoDependencySourceSpecial(specType, start, paths); |
1560 | *end = savec; | | 1560 | *end = savec; |
1561 | if (savec != '\0') | | 1561 | if (savec != '\0') |
1562 | end++; | | 1562 | end++; |
1563 | pp_skip_whitespace(&end); | | 1563 | pp_skip_whitespace(&end); |
1564 | start = end; | | 1564 | start = end; |
1565 | } | | 1565 | } |
1566 | } | | 1566 | } |
1567 | | | 1567 | |
1568 | static Boolean | | 1568 | static Boolean |
1569 | ParseDoDependencySourcesMundane(char *start, char *end, | | 1569 | ParseDoDependencySourcesMundane(char *start, char *end, |
1570 | ParseSpecial specType, GNodeType tOp) | | 1570 | ParseSpecial specType, GNodeType tOp) |
1571 | { | | 1571 | { |
1572 | while (*start) { | | 1572 | while (*start) { |
1573 | /* | | 1573 | /* |
1574 | * The targets take real sources, so we must beware of archive | | 1574 | * The targets take real sources, so we must beware of archive |
1575 | * specifications (i.e. things with left parentheses in them) | | 1575 | * specifications (i.e. things with left parentheses in them) |
1576 | * and handle them accordingly. | | 1576 | * and handle them accordingly. |
1577 | */ | | 1577 | */ |
1578 | for (; *end && !ch_isspace(*end); end++) { | | 1578 | for (; *end && !ch_isspace(*end); end++) { |
1579 | if (*end == '(' && end > start && end[-1] != '$') { | | 1579 | if (*end == '(' && end > start && end[-1] != '$') { |
1580 | /* | | 1580 | /* |
1581 | * Only stop for a left parenthesis if it isn't at the | | 1581 | * Only stop for a left parenthesis if it isn't at the |
1582 | * start of a word (that'll be for variable changes | | 1582 | * start of a word (that'll be for variable changes |
1583 | * later) and isn't preceded by a dollar sign (a dynamic | | 1583 | * later) and isn't preceded by a dollar sign (a dynamic |
1584 | * source). | | 1584 | * source). |
1585 | */ | | 1585 | */ |
1586 | break; | | 1586 | break; |
1587 | } | | 1587 | } |
1588 | } | | 1588 | } |
1589 | | | 1589 | |
1590 | if (*end == '(') { | | 1590 | if (*end == '(') { |
1591 | GNodeList *sources = Lst_New(); | | 1591 | GNodeList *sources = Lst_New(); |
1592 | if (!Arch_ParseArchive(&start, sources, VAR_CMDLINE)) { | | 1592 | if (!Arch_ParseArchive(&start, sources, VAR_CMDLINE)) { |
1593 | Parse_Error(PARSE_FATAL, | | 1593 | Parse_Error(PARSE_FATAL, |
1594 | "Error in source archive spec \"%s\"", start); | | 1594 | "Error in source archive spec \"%s\"", start); |
1595 | return FALSE; | | 1595 | return FALSE; |
1596 | } | | 1596 | } |
1597 | | | 1597 | |
1598 | while (!Lst_IsEmpty(sources)) { | | 1598 | while (!Lst_IsEmpty(sources)) { |
1599 | GNode *gn = Lst_Dequeue(sources); | | 1599 | GNode *gn = Lst_Dequeue(sources); |
1600 | ParseDoSrc(tOp, gn->name, specType); | | 1600 | ParseDoSrc(tOp, gn->name, specType); |
1601 | } | | 1601 | } |
1602 | Lst_Free(sources); | | 1602 | Lst_Free(sources); |
1603 | end = start; | | 1603 | end = start; |
1604 | } else { | | 1604 | } else { |
1605 | if (*end) { | | 1605 | if (*end) { |
1606 | *end = '\0'; | | 1606 | *end = '\0'; |
1607 | end++; | | 1607 | end++; |
1608 | } | | 1608 | } |
1609 | | | 1609 | |
1610 | ParseDoSrc(tOp, start, specType); | | 1610 | ParseDoSrc(tOp, start, specType); |
1611 | } | | 1611 | } |
1612 | pp_skip_whitespace(&end); | | 1612 | pp_skip_whitespace(&end); |
1613 | start = end; | | 1613 | start = end; |
1614 | } | | 1614 | } |
1615 | return TRUE; | | 1615 | return TRUE; |
1616 | } | | 1616 | } |
1617 | | | 1617 | |
1618 | /* Parse a dependency line consisting of targets, followed by a dependency | | 1618 | /* Parse a dependency line consisting of targets, followed by a dependency |
1619 | * operator, optionally followed by sources. | | 1619 | * operator, optionally followed by sources. |
1620 | * | | 1620 | * |
1621 | * The nodes of the sources are linked as children to the nodes of the | | 1621 | * The nodes of the sources are linked as children to the nodes of the |
1622 | * targets. Nodes are created as necessary. | | 1622 | * targets. Nodes are created as necessary. |
1623 | * | | 1623 | * |
1624 | * The operator is applied to each node in the global 'targets' list, | | 1624 | * The operator is applied to each node in the global 'targets' list, |
1625 | * which is where the nodes found for the targets are kept, by means of | | 1625 | * which is where the nodes found for the targets are kept, by means of |
1626 | * the ParseDoOp function. | | 1626 | * the ParseDoOp function. |
1627 | * | | 1627 | * |
1628 | * The sources are parsed in much the same way as the targets, except | | 1628 | * The sources are parsed in much the same way as the targets, except |
1629 | * that they are expanded using the wildcarding scheme of the C-Shell, | | 1629 | * that they are expanded using the wildcarding scheme of the C-Shell, |
1630 | * and all instances of the resulting words in the list of all targets | | 1630 | * and all instances of the resulting words in the list of all targets |
1631 | * are found. Each of the resulting nodes is then linked to each of the | | 1631 | * are found. Each of the resulting nodes is then linked to each of the |
1632 | * targets as one of its children. | | 1632 | * targets as one of its children. |
1633 | * | | 1633 | * |
1634 | * Certain targets and sources such as .PHONY or .PRECIOUS are handled | | 1634 | * Certain targets and sources such as .PHONY or .PRECIOUS are handled |
1635 | * specially. These are the ones detailed by the specType variable. | | 1635 | * specially. These are the ones detailed by the specType variable. |
1636 | * | | 1636 | * |
1637 | * The storing of transformation rules such as '.c.o' is also taken care of | | 1637 | * The storing of transformation rules such as '.c.o' is also taken care of |
1638 | * here. A target is recognized as a transformation rule by calling | | 1638 | * here. A target is recognized as a transformation rule by calling |
1639 | * Suff_IsTransform. If it is a transformation rule, its node is gotten | | 1639 | * Suff_IsTransform. If it is a transformation rule, its node is gotten |
1640 | * from the suffix module via Suff_AddTransform rather than the standard | | 1640 | * from the suffix module via Suff_AddTransform rather than the standard |
1641 | * Targ_FindNode in the target module. | | 1641 | * Targ_FindNode in the target module. |
1642 | */ | | 1642 | */ |
1643 | static void | | 1643 | static void |
1644 | ParseDoDependency(char *line) | | 1644 | ParseDoDependency(char *line) |
1645 | { | | 1645 | { |
1646 | char *cp; /* our current position */ | | 1646 | char *cp; /* our current position */ |
1647 | GNodeType op; /* the operator on the line */ | | 1647 | GNodeType op; /* the operator on the line */ |
1648 | SearchPathList *paths; /* search paths to alter when parsing | | 1648 | SearchPathList *paths; /* search paths to alter when parsing |
1649 | * a list of .PATH targets */ | | 1649 | * a list of .PATH targets */ |
1650 | int tOp; /* operator from special target */ | | 1650 | int tOp; /* operator from special target */ |
1651 | StringList *curTargs; /* target names to be found and added | | 1651 | StringList *curTargs; /* target names to be found and added |
1652 | * to the targets list */ | | 1652 | * to the targets list */ |
1653 | char *lstart = line; | | 1653 | char *lstart = line; |
1654 | | | 1654 | |
1655 | /* | | 1655 | /* |
1656 | * specType contains the SPECial TYPE of the current target. It is SP_NOT | | 1656 | * specType contains the SPECial TYPE of the current target. It is SP_NOT |
1657 | * if the target is unspecial. If it *is* special, however, the children | | 1657 | * if the target is unspecial. If it *is* special, however, the children |
1658 | * are linked as children of the parent but not vice versa. | | 1658 | * are linked as children of the parent but not vice versa. |
1659 | */ | | 1659 | */ |
1660 | ParseSpecial specType = SP_NOT; | | 1660 | ParseSpecial specType = SP_NOT; |
1661 | | | 1661 | |
1662 | DEBUG1(PARSE, "ParseDoDependency(%s)\n", line); | | 1662 | DEBUG1(PARSE, "ParseDoDependency(%s)\n", line); |
1663 | tOp = 0; | | 1663 | tOp = 0; |
1664 | | | 1664 | |
1665 | paths = NULL; | | 1665 | paths = NULL; |
1666 | | | 1666 | |
1667 | curTargs = Lst_New(); | | 1667 | curTargs = Lst_New(); |
1668 | | | 1668 | |
1669 | /* | | 1669 | /* |
1670 | * First, grind through the targets. | | 1670 | * First, grind through the targets. |
1671 | */ | | 1671 | */ |
1672 | if (!ParseDoDependencyTargets(&cp, &line, lstart, &specType, &tOp, &paths, | | 1672 | if (!ParseDoDependencyTargets(&cp, &line, lstart, &specType, &tOp, &paths, |
1673 | curTargs)) | | 1673 | curTargs)) |
1674 | goto out; | | 1674 | goto out; |
1675 | | | 1675 | |
1676 | /* | | 1676 | /* |
1677 | * Don't need the list of target names anymore... | | 1677 | * Don't need the list of target names anymore... |
1678 | */ | | 1678 | */ |
1679 | Lst_Free(curTargs); | | 1679 | Lst_Free(curTargs); |
1680 | curTargs = NULL; | | 1680 | curTargs = NULL; |
1681 | | | 1681 | |
1682 | if (!Lst_IsEmpty(targets)) | | 1682 | if (!Lst_IsEmpty(targets)) |
1683 | ParseDoDependencyCheckSpec(specType); | | 1683 | ParseDoDependencyCheckSpec(specType); |
1684 | | | 1684 | |
1685 | /* | | 1685 | /* |
1686 | * Have now parsed all the target names. Must parse the operator next. | | 1686 | * Have now parsed all the target names. Must parse the operator next. |
1687 | */ | | 1687 | */ |
1688 | if (!ParseDoDependencyParseOp(&cp, lstart, &op)) | | 1688 | if (!ParseDoDependencyParseOp(&cp, lstart, &op)) |
1689 | goto out; | | 1689 | goto out; |
1690 | | | 1690 | |
1691 | /* | | 1691 | /* |
1692 | * Apply the operator to the target. This is how we remember which | | 1692 | * Apply the operator to the target. This is how we remember which |
1693 | * operator a target was defined with. It fails if the operator | | 1693 | * operator a target was defined with. It fails if the operator |
1694 | * used isn't consistent across all references. | | 1694 | * used isn't consistent across all references. |
1695 | */ | | 1695 | */ |
1696 | ApplyDependencyOperator(op); | | 1696 | ApplyDependencyOperator(op); |
1697 | | | 1697 | |
1698 | /* | | 1698 | /* |
1699 | * Onward to the sources. | | 1699 | * Onward to the sources. |
1700 | * | | 1700 | * |
1701 | * LINE will now point to the first source word, if any, or the | | 1701 | * LINE will now point to the first source word, if any, or the |
1702 | * end of the string if not. | | 1702 | * end of the string if not. |
1703 | */ | | 1703 | */ |
1704 | pp_skip_whitespace(&cp); | | 1704 | pp_skip_whitespace(&cp); |
1705 | line = cp; | | 1705 | line = cp; |
1706 | | | 1706 | |
1707 | /* | | 1707 | /* |
1708 | * Several special targets take different actions if present with no | | 1708 | * Several special targets take different actions if present with no |
1709 | * sources: | | 1709 | * sources: |
1710 | * a .SUFFIXES line with no sources clears out all old suffixes | | 1710 | * a .SUFFIXES line with no sources clears out all old suffixes |
1711 | * a .PRECIOUS line makes all targets precious | | 1711 | * a .PRECIOUS line makes all targets precious |
1712 | * a .IGNORE line ignores errors for all targets | | 1712 | * a .IGNORE line ignores errors for all targets |
1713 | * a .SILENT line creates silence when making all targets | | 1713 | * a .SILENT line creates silence when making all targets |
1714 | * a .PATH removes all directories from the search path(s). | | 1714 | * a .PATH removes all directories from the search path(s). |
1715 | */ | | 1715 | */ |
1716 | if (!*line) { | | 1716 | if (!*line) { |
1717 | ParseDoDependencySourcesEmpty(specType, paths); | | 1717 | ParseDoDependencySourcesEmpty(specType, paths); |
1718 | } else if (specType == SP_MFLAGS) { | | 1718 | } else if (specType == SP_MFLAGS) { |
1719 | /* | | 1719 | /* |
1720 | * Call on functions in main.c to deal with these arguments and | | 1720 | * Call on functions in main.c to deal with these arguments and |
1721 | * set the initial character to a null-character so the loop to | | 1721 | * set the initial character to a null-character so the loop to |
1722 | * get sources won't get anything | | 1722 | * get sources won't get anything |
1723 | */ | | 1723 | */ |
1724 | Main_ParseArgLine(line); | | 1724 | Main_ParseArgLine(line); |
1725 | *line = '\0'; | | 1725 | *line = '\0'; |
1726 | } else if (specType == SP_SHELL) { | | 1726 | } else if (specType == SP_SHELL) { |
1727 | if (!Job_ParseShell(line)) { | | 1727 | if (!Job_ParseShell(line)) { |
1728 | Parse_Error(PARSE_FATAL, "improper shell specification"); | | 1728 | Parse_Error(PARSE_FATAL, "improper shell specification"); |
1729 | goto out; | | 1729 | goto out; |
1730 | } | | 1730 | } |
1731 | *line = '\0'; | | 1731 | *line = '\0'; |
1732 | } else if (specType == SP_NOTPARALLEL || specType == SP_SINGLESHELL || | | 1732 | } else if (specType == SP_NOTPARALLEL || specType == SP_SINGLESHELL || |
1733 | specType == SP_DELETE_ON_ERROR) { | | 1733 | specType == SP_DELETE_ON_ERROR) { |
1734 | *line = '\0'; | | 1734 | *line = '\0'; |
1735 | } | | 1735 | } |
1736 | | | 1736 | |
1737 | /* | | 1737 | /* |
1738 | * NOW GO FOR THE SOURCES | | 1738 | * NOW GO FOR THE SOURCES |
1739 | */ | | 1739 | */ |
1740 | if (specType == SP_SUFFIXES || specType == SP_PATH || | | 1740 | if (specType == SP_SUFFIXES || specType == SP_PATH || |
1741 | specType == SP_INCLUDES || specType == SP_LIBS || | | 1741 | specType == SP_INCLUDES || specType == SP_LIBS || |
1742 | specType == SP_NULL || specType == SP_OBJDIR) | | 1742 | specType == SP_NULL || specType == SP_OBJDIR) |
1743 | { | | 1743 | { |
1744 | ParseDoDependencySourcesSpecial(line, cp, specType, paths); | | 1744 | ParseDoDependencySourcesSpecial(line, cp, specType, paths); |
1745 | if (paths) { | | 1745 | if (paths) { |
1746 | Lst_Free(paths); | | 1746 | Lst_Free(paths); |
1747 | paths = NULL; | | 1747 | paths = NULL; |
1748 | } | | 1748 | } |
1749 | if (specType == SP_PATH) | | 1749 | if (specType == SP_PATH) |
1750 | Dir_SetPATH(); | | 1750 | Dir_SetPATH(); |
1751 | } else { | | 1751 | } else { |
1752 | assert(paths == NULL); | | 1752 | assert(paths == NULL); |
1753 | if (!ParseDoDependencySourcesMundane(line, cp, specType, tOp)) | | 1753 | if (!ParseDoDependencySourcesMundane(line, cp, specType, tOp)) |
1754 | goto out; | | 1754 | goto out; |
1755 | } | | 1755 | } |
1756 | | | 1756 | |
1757 | FindMainTarget(); | | 1757 | FindMainTarget(); |
1758 | | | 1758 | |
1759 | out: | | 1759 | out: |
1760 | if (paths != NULL) | | 1760 | if (paths != NULL) |
1761 | Lst_Free(paths); | | 1761 | Lst_Free(paths); |
1762 | if (curTargs != NULL) | | 1762 | if (curTargs != NULL) |
1763 | Lst_Free(curTargs); | | 1763 | Lst_Free(curTargs); |
1764 | } | | 1764 | } |
1765 | | | 1765 | |
1766 | typedef struct VarAssignParsed { | | 1766 | typedef struct VarAssignParsed { |
1767 | const char *nameStart; /* unexpanded */ | | 1767 | const char *nameStart; /* unexpanded */ |
1768 | const char *nameEnd; /* before operator adjustment */ | | 1768 | const char *nameEnd; /* before operator adjustment */ |
1769 | const char *eq; /* the '=' of the assignment operator */ | | 1769 | const char *eq; /* the '=' of the assignment operator */ |
1770 | } VarAssignParsed; | | 1770 | } VarAssignParsed; |
1771 | | | 1771 | |
1772 | /* Determine the assignment operator and adjust the end of the variable | | 1772 | /* Determine the assignment operator and adjust the end of the variable |
1773 | * name accordingly. */ | | 1773 | * name accordingly. */ |
1774 | static void | | 1774 | static void |
1775 | AdjustVarassignOp(const VarAssignParsed *pvar, const char *value, | | 1775 | AdjustVarassignOp(const VarAssignParsed *pvar, const char *value, |
1776 | VarAssign *out_var) | | 1776 | VarAssign *out_var) |
1777 | { | | 1777 | { |
1778 | const char *op = pvar->eq; | | 1778 | const char *op = pvar->eq; |
1779 | const char * const name = pvar->nameStart; | | 1779 | const char * const name = pvar->nameStart; |
1780 | VarAssignOp type; | | 1780 | VarAssignOp type; |
1781 | | | 1781 | |
1782 | if (op > name && op[-1] == '+') { | | 1782 | if (op > name && op[-1] == '+') { |
1783 | type = VAR_APPEND; | | 1783 | type = VAR_APPEND; |
1784 | op--; | | 1784 | op--; |
1785 | | | 1785 | |
1786 | } else if (op > name && op[-1] == '?') { | | 1786 | } else if (op > name && op[-1] == '?') { |
1787 | op--; | | 1787 | op--; |
1788 | type = VAR_DEFAULT; | | 1788 | type = VAR_DEFAULT; |
1789 | | | 1789 | |
1790 | } else if (op > name && op[-1] == ':') { | | 1790 | } else if (op > name && op[-1] == ':') { |
1791 | op--; | | 1791 | op--; |
1792 | type = VAR_SUBST; | | 1792 | type = VAR_SUBST; |
1793 | | | 1793 | |
1794 | } else if (op > name && op[-1] == '!') { | | 1794 | } else if (op > name && op[-1] == '!') { |
1795 | op--; | | 1795 | op--; |
1796 | type = VAR_SHELL; | | 1796 | type = VAR_SHELL; |
1797 | | | 1797 | |
1798 | } else { | | 1798 | } else { |
1799 | type = VAR_NORMAL; | | 1799 | type = VAR_NORMAL; |
1800 | #ifdef SUNSHCMD | | 1800 | #ifdef SUNSHCMD |
1801 | while (op > name && ch_isspace(op[-1])) | | 1801 | while (op > name && ch_isspace(op[-1])) |
1802 | op--; | | 1802 | op--; |
1803 | | | 1803 | |
1804 | if (op >= name + 3 && op[-3] == ':' && op[-2] == 's' && op[-1] == 'h') { | | 1804 | if (op >= name + 3 && op[-3] == ':' && op[-2] == 's' && op[-1] == 'h') { |
1805 | type = VAR_SHELL; | | 1805 | type = VAR_SHELL; |
1806 | op -= 3; | | 1806 | op -= 3; |
1807 | } | | 1807 | } |
1808 | #endif | | 1808 | #endif |
1809 | } | | 1809 | } |
1810 | | | 1810 | |
1811 | { | | 1811 | { |
1812 | const char *nameEnd = pvar->nameEnd < op ? pvar->nameEnd : op; | | 1812 | const char *nameEnd = pvar->nameEnd < op ? pvar->nameEnd : op; |
1813 | out_var->varname = bmake_strsedup(pvar->nameStart, nameEnd); | | 1813 | out_var->varname = bmake_strsedup(pvar->nameStart, nameEnd); |
1814 | out_var->op = type; | | 1814 | out_var->op = type; |
1815 | out_var->value = value; | | 1815 | out_var->value = value; |
1816 | } | | 1816 | } |
1817 | } | | 1817 | } |
1818 | | | 1818 | |
1819 | /* Parse a variable assignment, consisting of a single-word variable name, | | 1819 | /* Parse a variable assignment, consisting of a single-word variable name, |
1820 | * optional whitespace, an assignment operator, optional whitespace and the | | 1820 | * optional whitespace, an assignment operator, optional whitespace and the |
1821 | * variable value. | | 1821 | * variable value. |
1822 | * | | 1822 | * |
1823 | * Note: There is a lexical ambiguity with assignment modifier characters | | 1823 | * Note: There is a lexical ambiguity with assignment modifier characters |
1824 | * in variable names. This routine interprets the character before the = | | 1824 | * in variable names. This routine interprets the character before the = |
1825 | * as a modifier. Therefore, an assignment like | | 1825 | * as a modifier. Therefore, an assignment like |
1826 | * C++=/usr/bin/CC | | 1826 | * C++=/usr/bin/CC |
1827 | * is interpreted as "C+ +=" instead of "C++ =". | | 1827 | * is interpreted as "C+ +=" instead of "C++ =". |
1828 | * | | 1828 | * |
1829 | * Used for both lines in a file and command line arguments. */ | | 1829 | * Used for both lines in a file and command line arguments. */ |
1830 | Boolean | | 1830 | Boolean |
1831 | Parse_IsVar(const char *p, VarAssign *out_var) | | 1831 | Parse_IsVar(const char *p, VarAssign *out_var) |
1832 | { | | 1832 | { |
1833 | VarAssignParsed pvar; | | 1833 | VarAssignParsed pvar; |
1834 | const char *firstSpace = NULL; | | 1834 | const char *firstSpace = NULL; |
1835 | char ch; | | 1835 | char ch; |
1836 | int level = 0; | | 1836 | int level = 0; |
1837 | | | 1837 | |
1838 | /* Skip to variable name */ | | 1838 | /* Skip to variable name */ |
1839 | while (*p == ' ' || *p == '\t') | | 1839 | while (*p == ' ' || *p == '\t') |
1840 | p++; | | 1840 | p++; |
1841 | | | 1841 | |
1842 | /* During parsing, the '+' of the '+=' operator is initially parsed | | 1842 | /* During parsing, the '+' of the '+=' operator is initially parsed |
1843 | * as part of the variable name. It is later corrected, as is the ':sh' | | 1843 | * as part of the variable name. It is later corrected, as is the ':sh' |
1844 | * modifier. Of these two (nameEnd and op), the earlier one determines the | | 1844 | * modifier. Of these two (nameEnd and op), the earlier one determines the |
1845 | * actual end of the variable name. */ | | 1845 | * actual end of the variable name. */ |
1846 | pvar.nameStart = p; | | 1846 | pvar.nameStart = p; |
1847 | #ifdef CLEANUP | | 1847 | #ifdef CLEANUP |
1848 | pvar.nameEnd = NULL; | | 1848 | pvar.nameEnd = NULL; |
1849 | pvar.eq = NULL; | | 1849 | pvar.eq = NULL; |
1850 | #endif | | 1850 | #endif |
1851 | | | 1851 | |
1852 | /* Scan for one of the assignment operators outside a variable expansion */ | | 1852 | /* Scan for one of the assignment operators outside a variable expansion */ |
1853 | while ((ch = *p++) != 0) { | | 1853 | while ((ch = *p++) != 0) { |
1854 | if (ch == '(' || ch == '{') { | | 1854 | if (ch == '(' || ch == '{') { |
1855 | level++; | | 1855 | level++; |
1856 | continue; | | 1856 | continue; |
1857 | } | | 1857 | } |
1858 | if (ch == ')' || ch == '}') { | | 1858 | if (ch == ')' || ch == '}') { |
1859 | level--; | | 1859 | level--; |
1860 | continue; | | 1860 | continue; |
1861 | } | | 1861 | } |
1862 | | | 1862 | |
1863 | if (level != 0) | | 1863 | if (level != 0) |
1864 | continue; | | 1864 | continue; |
1865 | | | 1865 | |
1866 | if (ch == ' ' || ch == '\t') | | 1866 | if (ch == ' ' || ch == '\t') |
1867 | if (firstSpace == NULL) | | 1867 | if (firstSpace == NULL) |
1868 | firstSpace = p - 1; | | 1868 | firstSpace = p - 1; |
1869 | while (ch == ' ' || ch == '\t') | | 1869 | while (ch == ' ' || ch == '\t') |
1870 | ch = *p++; | | 1870 | ch = *p++; |
1871 | | | 1871 | |
1872 | #ifdef SUNSHCMD | | 1872 | #ifdef SUNSHCMD |
1873 | if (ch == ':' && strncmp(p, "sh", 2) == 0) { | | 1873 | if (ch == ':' && strncmp(p, "sh", 2) == 0) { |
1874 | p += 2; | | 1874 | p += 2; |
1875 | continue; | | 1875 | continue; |
1876 | } | | 1876 | } |
1877 | #endif | | 1877 | #endif |
1878 | if (ch == '=') { | | 1878 | if (ch == '=') { |
1879 | pvar.eq = p - 1; | | 1879 | pvar.eq = p - 1; |
1880 | pvar.nameEnd = firstSpace != NULL ? firstSpace : p - 1; | | 1880 | pvar.nameEnd = firstSpace != NULL ? firstSpace : p - 1; |
1881 | cpp_skip_whitespace(&p); | | 1881 | cpp_skip_whitespace(&p); |
1882 | AdjustVarassignOp(&pvar, p, out_var); | | 1882 | AdjustVarassignOp(&pvar, p, out_var); |
1883 | return TRUE; | | 1883 | return TRUE; |
1884 | } | | 1884 | } |
1885 | if (*p == '=' && (ch == '+' || ch == ':' || ch == '?' || ch == '!')) { | | 1885 | if (*p == '=' && (ch == '+' || ch == ':' || ch == '?' || ch == '!')) { |
1886 | pvar.eq = p; | | 1886 | pvar.eq = p; |
1887 | pvar.nameEnd = firstSpace != NULL ? firstSpace : p; | | 1887 | pvar.nameEnd = firstSpace != NULL ? firstSpace : p; |
1888 | p++; | | 1888 | p++; |
1889 | cpp_skip_whitespace(&p); | | 1889 | cpp_skip_whitespace(&p); |
1890 | AdjustVarassignOp(&pvar, p, out_var); | | 1890 | AdjustVarassignOp(&pvar, p, out_var); |
1891 | return TRUE; | | 1891 | return TRUE; |
1892 | } | | 1892 | } |
1893 | if (firstSpace != NULL) | | 1893 | if (firstSpace != NULL) |
1894 | return FALSE; | | 1894 | return FALSE; |
1895 | } | | 1895 | } |
1896 | | | 1896 | |
1897 | return FALSE; | | 1897 | return FALSE; |
1898 | } | | 1898 | } |
1899 | | | 1899 | |
1900 | static void | | 1900 | static void |
1901 | VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt) | | 1901 | VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt) |
1902 | { | | 1902 | { |
1903 | if (DEBUG(LINT)) { | | 1903 | if (DEBUG(LINT)) { |
1904 | if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) { | | 1904 | if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) { |
1905 | /* Check for syntax errors such as unclosed expressions or | | 1905 | /* Check for syntax errors such as unclosed expressions or |
1906 | * unknown modifiers. */ | | 1906 | * unknown modifiers. */ |
1907 | char *expandedValue; | | 1907 | char *expandedValue; |
1908 | | | 1908 | |
1909 | (void)Var_Subst(uvalue, ctxt, VARE_NONE, &expandedValue); | | 1909 | (void)Var_Subst(uvalue, ctxt, VARE_NONE, &expandedValue); |
1910 | /* TODO: handle errors */ | | 1910 | /* TODO: handle errors */ |
1911 | free(expandedValue); | | 1911 | free(expandedValue); |
1912 | } | | 1912 | } |
1913 | } | | 1913 | } |
1914 | } | | 1914 | } |
1915 | | | 1915 | |
1916 | static void | | 1916 | static void |
1917 | VarAssign_EvalSubst(const char *name, const char *uvalue, GNode *ctxt, | | 1917 | VarAssign_EvalSubst(const char *name, const char *uvalue, GNode *ctxt, |
1918 | const char **out_avalue, void **out_avalue_freeIt) | | 1918 | const char **out_avalue, void **out_avalue_freeIt) |
1919 | { | | 1919 | { |
1920 | const char *avalue = uvalue; | | 1920 | const char *avalue = uvalue; |
1921 | char *evalue; | | 1921 | char *evalue; |
1922 | /* | | 1922 | /* |
1923 | * Allow variables in the old value to be undefined, but leave their | | 1923 | * Allow variables in the old value to be undefined, but leave their |
1924 | * expressions alone -- this is done by forcing oldVars to be false. | | 1924 | * expressions alone -- this is done by forcing oldVars to be false. |
1925 | * XXX: This can cause recursive variables, but that's not hard to do, | | 1925 | * XXX: This can cause recursive variables, but that's not hard to do, |
1926 | * and this allows someone to do something like | | 1926 | * and this allows someone to do something like |
1927 | * | | 1927 | * |
1928 | * CFLAGS = $(.INCLUDES) | | 1928 | * CFLAGS = $(.INCLUDES) |
1929 | * CFLAGS := -I.. $(CFLAGS) | | 1929 | * CFLAGS := -I.. $(CFLAGS) |
1930 | * | | 1930 | * |
1931 | * And not get an error. | | 1931 | * And not get an error. |
1932 | */ | | 1932 | */ |
1933 | Boolean oldOldVars = oldVars; | | 1933 | Boolean oldOldVars = oldVars; |
1934 | | | 1934 | |
1935 | oldVars = FALSE; | | 1935 | oldVars = FALSE; |
1936 | | | 1936 | |
1937 | /* | | 1937 | /* |
1938 | * make sure that we set the variable the first time to nothing | | 1938 | * make sure that we set the variable the first time to nothing |
1939 | * so that it gets substituted! | | 1939 | * so that it gets substituted! |
1940 | */ | | 1940 | */ |
1941 | if (!Var_Exists(name, ctxt)) | | 1941 | if (!Var_Exists(name, ctxt)) |
1942 | Var_Set(name, "", ctxt); | | 1942 | Var_Set(name, "", ctxt); |
1943 | | | 1943 | |
1944 | (void)Var_Subst(uvalue, ctxt, VARE_WANTRES|VARE_ASSIGN, &evalue); | | 1944 | (void)Var_Subst(uvalue, ctxt, VARE_WANTRES|VARE_ASSIGN, &evalue); |
1945 | /* TODO: handle errors */ | | 1945 | /* TODO: handle errors */ |
1946 | oldVars = oldOldVars; | | 1946 | oldVars = oldOldVars; |
1947 | avalue = evalue; | | 1947 | avalue = evalue; |
1948 | Var_Set(name, avalue, ctxt); | | 1948 | Var_Set(name, avalue, ctxt); |
1949 | | | 1949 | |
1950 | *out_avalue = avalue; | | 1950 | *out_avalue = avalue; |
1951 | *out_avalue_freeIt = evalue; | | 1951 | *out_avalue_freeIt = evalue; |
1952 | } | | 1952 | } |
1953 | | | 1953 | |
1954 | static void | | 1954 | static void |
1955 | VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt, | | 1955 | VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt, |
1956 | const char **out_avalue, void **out_avalue_freeIt) | | 1956 | const char **out_avalue, void **out_avalue_freeIt) |
1957 | { | | 1957 | { |
1958 | const char *cmd, *errfmt; | | 1958 | const char *cmd, *errfmt; |
1959 | char *cmdOut; | | 1959 | char *cmdOut; |
1960 | void *cmd_freeIt = NULL; | | 1960 | void *cmd_freeIt = NULL; |
1961 | | | 1961 | |
1962 | cmd = uvalue; | | 1962 | cmd = uvalue; |
1963 | if (strchr(cmd, '$') != NULL) { | | 1963 | if (strchr(cmd, '$') != NULL) { |
1964 | char *ecmd; | | 1964 | char *ecmd; |
1965 | (void)Var_Subst(cmd, VAR_CMDLINE, VARE_UNDEFERR | VARE_WANTRES, &ecmd); | | 1965 | (void)Var_Subst(cmd, VAR_CMDLINE, VARE_UNDEFERR | VARE_WANTRES, &ecmd); |
1966 | /* TODO: handle errors */ | | 1966 | /* TODO: handle errors */ |
1967 | cmd = cmd_freeIt = ecmd; | | 1967 | cmd = cmd_freeIt = ecmd; |
1968 | } | | 1968 | } |
1969 | | | 1969 | |
1970 | cmdOut = Cmd_Exec(cmd, &errfmt); | | 1970 | cmdOut = Cmd_Exec(cmd, &errfmt); |
1971 | Var_Set(name, cmdOut, ctxt); | | 1971 | Var_Set(name, cmdOut, ctxt); |
1972 | *out_avalue = *out_avalue_freeIt = cmdOut; | | 1972 | *out_avalue = *out_avalue_freeIt = cmdOut; |
1973 | | | 1973 | |
1974 | if (errfmt) | | 1974 | if (errfmt) |
1975 | Parse_Error(PARSE_WARNING, errfmt, cmd); | | 1975 | Parse_Error(PARSE_WARNING, errfmt, cmd); |
1976 | | | 1976 | |
1977 | free(cmd_freeIt); | | 1977 | free(cmd_freeIt); |
1978 | } | | 1978 | } |
1979 | | | 1979 | |
1980 | /* Perform a variable assignment. | | 1980 | /* Perform a variable assignment. |
1981 | * | | 1981 | * |
1982 | * The actual value of the variable is returned in *out_avalue and | | 1982 | * The actual value of the variable is returned in *out_avalue and |
1983 | * *out_avalue_freeIt. Especially for VAR_SUBST and VAR_SHELL this can differ | | 1983 | * *out_avalue_freeIt. Especially for VAR_SUBST and VAR_SHELL this can differ |
1984 | * from the literal value. | | 1984 | * from the literal value. |
1985 | * | | 1985 | * |
1986 | * Return whether the assignment was actually done. The assignment is only | | 1986 | * Return whether the assignment was actually done. The assignment is only |
1987 | * skipped if the operator is '?=' and the variable already exists. */ | | 1987 | * skipped if the operator is '?=' and the variable already exists. */ |
1988 | static Boolean | | 1988 | static Boolean |
1989 | VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue, | | 1989 | VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue, |
1990 | GNode *ctxt, const char **out_avalue, void **out_avalue_freeIt) | | 1990 | GNode *ctxt, const char **out_avalue, void **out_avalue_freeIt) |
1991 | { | | 1991 | { |
1992 | const char *avalue = uvalue; | | 1992 | const char *avalue = uvalue; |
1993 | void *avalue_freeIt = NULL; | | 1993 | void *avalue_freeIt = NULL; |
1994 | | | 1994 | |
1995 | if (op == VAR_APPEND) { | | 1995 | if (op == VAR_APPEND) { |
1996 | Var_Append(name, uvalue, ctxt); | | 1996 | Var_Append(name, uvalue, ctxt); |
1997 | } else if (op == VAR_SUBST) { | | 1997 | } else if (op == VAR_SUBST) { |
1998 | VarAssign_EvalSubst(name, uvalue, ctxt, &avalue, &avalue_freeIt); | | 1998 | VarAssign_EvalSubst(name, uvalue, ctxt, &avalue, &avalue_freeIt); |
1999 | } else if (op == VAR_SHELL) { | | 1999 | } else if (op == VAR_SHELL) { |
2000 | VarAssign_EvalShell(name, uvalue, ctxt, &avalue, &avalue_freeIt); | | 2000 | VarAssign_EvalShell(name, uvalue, ctxt, &avalue, &avalue_freeIt); |
2001 | } else { | | 2001 | } else { |
2002 | if (op == VAR_DEFAULT && Var_Exists(name, ctxt)) { | | 2002 | if (op == VAR_DEFAULT && Var_Exists(name, ctxt)) { |
2003 | *out_avalue_freeIt = NULL; | | 2003 | *out_avalue_freeIt = NULL; |
2004 | return FALSE; | | 2004 | return FALSE; |
2005 | } | | 2005 | } |
2006 | | | 2006 | |
2007 | /* Normal assignment -- just do it. */ | | 2007 | /* Normal assignment -- just do it. */ |
2008 | Var_Set(name, uvalue, ctxt); | | 2008 | Var_Set(name, uvalue, ctxt); |
2009 | } | | 2009 | } |
2010 | | | 2010 | |
2011 | *out_avalue = avalue; | | 2011 | *out_avalue = avalue; |
2012 | *out_avalue_freeIt = avalue_freeIt; | | 2012 | *out_avalue_freeIt = avalue_freeIt; |
2013 | return TRUE; | | 2013 | return TRUE; |
2014 | } | | 2014 | } |
2015 | | | 2015 | |
2016 | static void | | 2016 | static void |
2017 | VarAssignSpecial(const char *name, const char *avalue) | | 2017 | VarAssignSpecial(const char *name, const char *avalue) |
2018 | { | | 2018 | { |
2019 | if (strcmp(name, MAKEOVERRIDES) == 0) | | 2019 | if (strcmp(name, MAKEOVERRIDES) == 0) |
2020 | Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */ | | 2020 | Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */ |
2021 | else if (strcmp(name, ".CURDIR") == 0) { | | 2021 | else if (strcmp(name, ".CURDIR") == 0) { |
2022 | /* | | 2022 | /* |
2023 | * Someone is being (too?) clever... | | 2023 | * Someone is being (too?) clever... |
2024 | * Let's pretend they know what they are doing and | | 2024 | * Let's pretend they know what they are doing and |
2025 | * re-initialize the 'cur' CachedDir. | | 2025 | * re-initialize the 'cur' CachedDir. |
2026 | */ | | 2026 | */ |
2027 | Dir_InitCur(avalue); | | 2027 | Dir_InitCur(avalue); |
2028 | Dir_SetPATH(); | | 2028 | Dir_SetPATH(); |
2029 | } else if (strcmp(name, MAKE_JOB_PREFIX) == 0) { | | 2029 | } else if (strcmp(name, MAKE_JOB_PREFIX) == 0) { |
2030 | Job_SetPrefix(); | | 2030 | Job_SetPrefix(); |
2031 | } else if (strcmp(name, MAKE_EXPORTED) == 0) { | | 2031 | } else if (strcmp(name, MAKE_EXPORTED) == 0) { |
2032 | Var_Export(avalue, FALSE); | | 2032 | Var_Export(avalue, FALSE); |
2033 | } | | 2033 | } |
2034 | } | | 2034 | } |
2035 | | | 2035 | |
2036 | /* Perform the variable variable assignment in the given context. */ | | 2036 | /* Perform the variable variable assignment in the given context. */ |
2037 | void | | 2037 | void |
2038 | Parse_DoVar(VarAssign *var, GNode *ctxt) | | 2038 | Parse_DoVar(VarAssign *var, GNode *ctxt) |
2039 | { | | 2039 | { |
2040 | const char *avalue; /* actual value (maybe expanded) */ | | 2040 | const char *avalue; /* actual value (maybe expanded) */ |
2041 | void *avalue_freeIt; | | 2041 | void *avalue_freeIt; |
2042 | | | 2042 | |
2043 | VarCheckSyntax(var->op, var->value, ctxt); | | 2043 | VarCheckSyntax(var->op, var->value, ctxt); |
2044 | if (VarAssign_Eval(var->varname, var->op, var->value, ctxt, | | 2044 | if (VarAssign_Eval(var->varname, var->op, var->value, ctxt, |
2045 | &avalue, &avalue_freeIt)) | | 2045 | &avalue, &avalue_freeIt)) |
2046 | VarAssignSpecial(var->varname, avalue); | | 2046 | VarAssignSpecial(var->varname, avalue); |
2047 | | | 2047 | |
2048 | free(avalue_freeIt); | | 2048 | free(avalue_freeIt); |
2049 | free(var->varname); | | 2049 | free(var->varname); |
2050 | } | | 2050 | } |
2051 | | | 2051 | |
2052 | | | 2052 | |
2053 | /* | | 2053 | /* |
2054 | * ParseMaybeSubMake -- | | 2054 | * ParseMaybeSubMake -- |
2055 | * Scan the command string to see if it a possible submake node | | 2055 | * Scan the command string to see if it a possible submake node |
2056 | * Input: | | 2056 | * Input: |
2057 | * cmd the command to scan | | 2057 | * cmd the command to scan |
2058 | * Results: | | 2058 | * Results: |
2059 | * TRUE if the command is possibly a submake, FALSE if not. | | 2059 | * TRUE if the command is possibly a submake, FALSE if not. |
2060 | */ | | 2060 | */ |
2061 | static Boolean | | 2061 | static Boolean |
2062 | ParseMaybeSubMake(const char *cmd) | | 2062 | ParseMaybeSubMake(const char *cmd) |
2063 | { | | 2063 | { |
2064 | size_t i; | | 2064 | size_t i; |
2065 | static struct { | | 2065 | static struct { |
2066 | const char *name; | | 2066 | const char *name; |
2067 | size_t len; | | 2067 | size_t len; |
2068 | } vals[] = { | | 2068 | } vals[] = { |
2069 | #define MKV(A) { A, sizeof(A) - 1 } | | 2069 | #define MKV(A) { A, sizeof(A) - 1 } |
2070 | MKV("${MAKE}"), | | 2070 | MKV("${MAKE}"), |
2071 | MKV("${.MAKE}"), | | 2071 | MKV("${.MAKE}"), |
2072 | MKV("$(MAKE)"), | | 2072 | MKV("$(MAKE)"), |
2073 | MKV("$(.MAKE)"), | | 2073 | MKV("$(.MAKE)"), |
2074 | MKV("make"), | | 2074 | MKV("make"), |
2075 | }; | | 2075 | }; |
2076 | for (i = 0; i < sizeof vals / sizeof vals[0]; i++) { | | 2076 | for (i = 0; i < sizeof vals / sizeof vals[0]; i++) { |
2077 | char *ptr; | | 2077 | char *ptr; |
2078 | if ((ptr = strstr(cmd, vals[i].name)) == NULL) | | 2078 | if ((ptr = strstr(cmd, vals[i].name)) == NULL) |
2079 | continue; | | 2079 | continue; |
2080 | if ((ptr == cmd || !ch_isalnum(ptr[-1])) | | 2080 | if ((ptr == cmd || !ch_isalnum(ptr[-1])) |
2081 | && !ch_isalnum(ptr[vals[i].len])) | | 2081 | && !ch_isalnum(ptr[vals[i].len])) |
2082 | return TRUE; | | 2082 | return TRUE; |
2083 | } | | 2083 | } |
2084 | return FALSE; | | 2084 | return FALSE; |
2085 | } | | 2085 | } |
2086 | | | 2086 | |
2087 | /* Append the command to the target node. | | 2087 | /* Append the command to the target node. |
2088 | * | | 2088 | * |
2089 | * The node may be marked as a submake node if the command is determined to | | 2089 | * The node may be marked as a submake node if the command is determined to |
2090 | * be that. */ | | 2090 | * be that. */ |
2091 | static void | | 2091 | static void |
2092 | ParseAddCmd(GNode *gn, char *cmd) | | 2092 | ParseAddCmd(GNode *gn, char *cmd) |
2093 | { | | 2093 | { |
2094 | /* Add to last (ie current) cohort for :: targets */ | | 2094 | /* Add to last (ie current) cohort for :: targets */ |
2095 | if ((gn->type & OP_DOUBLEDEP) && gn->cohorts->last != NULL) | | 2095 | if ((gn->type & OP_DOUBLEDEP) && gn->cohorts->last != NULL) |
2096 | gn = gn->cohorts->last->datum; | | 2096 | gn = gn->cohorts->last->datum; |
2097 | | | 2097 | |
2098 | /* if target already supplied, ignore commands */ | | 2098 | /* if target already supplied, ignore commands */ |
2099 | if (!(gn->type & OP_HAS_COMMANDS)) { | | 2099 | if (!(gn->type & OP_HAS_COMMANDS)) { |
2100 | Lst_Append(gn->commands, cmd); | | 2100 | Lst_Append(gn->commands, cmd); |
2101 | if (ParseMaybeSubMake(cmd)) | | 2101 | if (ParseMaybeSubMake(cmd)) |
2102 | gn->type |= OP_SUBMAKE; | | 2102 | gn->type |= OP_SUBMAKE; |
2103 | ParseMark(gn); | | 2103 | ParseMark(gn); |
2104 | } else { | | 2104 | } else { |
2105 | #if 0 | | 2105 | #if 0 |
2106 | /* XXX: We cannot do this until we fix the tree */ | | 2106 | /* XXX: We cannot do this until we fix the tree */ |
2107 | Lst_Append(gn->commands, cmd); | | 2107 | Lst_Append(gn->commands, cmd); |
2108 | Parse_Error(PARSE_WARNING, | | 2108 | Parse_Error(PARSE_WARNING, |
2109 | "overriding commands for target \"%s\"; " | | 2109 | "overriding commands for target \"%s\"; " |
2110 | "previous commands defined at %s: %d ignored", | | 2110 | "previous commands defined at %s: %d ignored", |
2111 | gn->name, gn->fname, gn->lineno); | | 2111 | gn->name, gn->fname, gn->lineno); |
2112 | #else | | 2112 | #else |
2113 | Parse_Error(PARSE_WARNING, | | 2113 | Parse_Error(PARSE_WARNING, |
2114 | "duplicate script for target \"%s\" ignored", | | 2114 | "duplicate script for target \"%s\" ignored", |
2115 | gn->name); | | 2115 | gn->name); |
2116 | ParseErrorInternal(gn->fname, (size_t)gn->lineno, PARSE_WARNING, | | 2116 | ParseErrorInternal(gn->fname, (size_t)gn->lineno, PARSE_WARNING, |
2117 | "using previous script for \"%s\" defined here", | | 2117 | "using previous script for \"%s\" defined here", |
2118 | gn->name); | | 2118 | gn->name); |
2119 | #endif | | 2119 | #endif |
2120 | } | | 2120 | } |
2121 | } | | 2121 | } |
2122 | | | 2122 | |
2123 | /* Add a directory to the path searched for included makefiles bracketed | | 2123 | /* Add a directory to the path searched for included makefiles bracketed |
2124 | * by double-quotes. */ | | 2124 | * by double-quotes. */ |
2125 | void | | 2125 | void |
2126 | Parse_AddIncludeDir(const char *dir) | | 2126 | Parse_AddIncludeDir(const char *dir) |
2127 | { | | 2127 | { |
2128 | (void)Dir_AddDir(parseIncPath, dir); | | 2128 | (void)Dir_AddDir(parseIncPath, dir); |
2129 | } | | 2129 | } |
2130 | | | 2130 | |
2131 | /* Push to another file. | | 2131 | /* Push to another file. |
2132 | * | | 2132 | * |
2133 | * The input is the line minus the '.'. A file spec is a string enclosed in | | 2133 | * The input is the line minus the '.'. A file spec is a string enclosed in |
2134 | * <> or "". The <> file is looked for only in sysIncPath. The "" file is | | 2134 | * <> or "". The <> file is looked for only in sysIncPath. The "" file is |
2135 | * first searched in the parsedir and then in the directories specified by | | 2135 | * first searched in the parsedir and then in the directories specified by |
2136 | * the -I command line options. | | 2136 | * the -I command line options. |
2137 | */ | | 2137 | */ |
2138 | static void | | 2138 | static void |
2139 | Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent) | | 2139 | Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent) |
2140 | { | | 2140 | { |
2141 | struct loadedfile *lf; | | 2141 | struct loadedfile *lf; |
2142 | char *fullname; /* full pathname of file */ | | 2142 | char *fullname; /* full pathname of file */ |
2143 | char *newName; | | 2143 | char *newName; |
2144 | char *prefEnd, *incdir; | | 2144 | char *prefEnd, *incdir; |
2145 | int fd; | | 2145 | int fd; |
2146 | int i; | | 2146 | int i; |
2147 | | | 2147 | |
2148 | /* | | 2148 | /* |
2149 | * Now we know the file's name and its search path, we attempt to | | 2149 | * Now we know the file's name and its search path, we attempt to |
2150 | * find the durn thing. A return of NULL indicates the file don't | | 2150 | * find the durn thing. A return of NULL indicates the file don't |
2151 | * exist. | | 2151 | * exist. |
2152 | */ | | 2152 | */ |
2153 | fullname = file[0] == '/' ? bmake_strdup(file) : NULL; | | 2153 | fullname = file[0] == '/' ? bmake_strdup(file) : NULL; |
2154 | | | 2154 | |
2155 | if (fullname == NULL && !isSystem) { | | 2155 | if (fullname == NULL && !isSystem) { |
2156 | /* | | 2156 | /* |
2157 | * Include files contained in double-quotes are first searched for | | 2157 | * Include files contained in double-quotes are first searched for |
2158 | * relative to the including file's location. We don't want to | | 2158 | * relative to the including file's location. We don't want to |
2159 | * cd there, of course, so we just tack on the old file's | | 2159 | * cd there, of course, so we just tack on the old file's |
2160 | * leading path components and call Dir_FindFile to see if | | 2160 | * leading path components and call Dir_FindFile to see if |
2161 | * we can locate the beast. | | 2161 | * we can locate the beast. |
2162 | */ | | 2162 | */ |
2163 | | | 2163 | |
2164 | incdir = bmake_strdup(CurFile()->fname); | | 2164 | incdir = bmake_strdup(CurFile()->fname); |
2165 | prefEnd = strrchr(incdir, '/'); | | 2165 | prefEnd = strrchr(incdir, '/'); |
2166 | if (prefEnd != NULL) { | | 2166 | if (prefEnd != NULL) { |
2167 | *prefEnd = '\0'; | | 2167 | *prefEnd = '\0'; |
2168 | /* Now do lexical processing of leading "../" on the filename */ | | 2168 | /* Now do lexical processing of leading "../" on the filename */ |
2169 | for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { | | 2169 | for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { |
2170 | prefEnd = strrchr(incdir + 1, '/'); | | 2170 | prefEnd = strrchr(incdir + 1, '/'); |
2171 | if (prefEnd == NULL || strcmp(prefEnd, "/..") == 0) | | 2171 | if (prefEnd == NULL || strcmp(prefEnd, "/..") == 0) |
2172 | break; | | 2172 | break; |
2173 | *prefEnd = '\0'; | | 2173 | *prefEnd = '\0'; |
2174 | } | | 2174 | } |
2175 | newName = str_concat3(incdir, "/", file + i); | | 2175 | newName = str_concat3(incdir, "/", file + i); |
2176 | fullname = Dir_FindFile(newName, parseIncPath); | | 2176 | fullname = Dir_FindFile(newName, parseIncPath); |
2177 | if (fullname == NULL) | | 2177 | if (fullname == NULL) |
2178 | fullname = Dir_FindFile(newName, dirSearchPath); | | 2178 | fullname = Dir_FindFile(newName, dirSearchPath); |
2179 | free(newName); | | 2179 | free(newName); |
2180 | } | | 2180 | } |
2181 | free(incdir); | | 2181 | free(incdir); |
2182 | | | 2182 | |
2183 | if (fullname == NULL) { | | 2183 | if (fullname == NULL) { |
2184 | /* | | 2184 | /* |
2185 | * Makefile wasn't found in same directory as included makefile. | | 2185 | * Makefile wasn't found in same directory as included makefile. |
2186 | * Search for it first on the -I search path, | | 2186 | * Search for it first on the -I search path, |
2187 | * then on the .PATH search path, if not found in a -I directory. | | 2187 | * then on the .PATH search path, if not found in a -I directory. |
2188 | * If we have a suffix specific path we should use that. | | 2188 | * If we have a suffix specific path we should use that. |
2189 | */ | | 2189 | */ |
2190 | char *suff; | | 2190 | char *suff; |
2191 | SearchPath *suffPath = NULL; | | 2191 | SearchPath *suffPath = NULL; |
2192 | | | 2192 | |
2193 | if ((suff = strrchr(file, '.'))) { | | 2193 | if ((suff = strrchr(file, '.'))) { |
2194 | suffPath = Suff_GetPath(suff); | | 2194 | suffPath = Suff_GetPath(suff); |
2195 | if (suffPath != NULL) { | | 2195 | if (suffPath != NULL) { |
2196 | fullname = Dir_FindFile(file, suffPath); | | 2196 | fullname = Dir_FindFile(file, suffPath); |
2197 | } | | 2197 | } |
2198 | } | | 2198 | } |
2199 | if (fullname == NULL) { | | 2199 | if (fullname == NULL) { |
2200 | fullname = Dir_FindFile(file, parseIncPath); | | 2200 | fullname = Dir_FindFile(file, parseIncPath); |
2201 | if (fullname == NULL) { | | 2201 | if (fullname == NULL) { |
2202 | fullname = Dir_FindFile(file, dirSearchPath); | | 2202 | fullname = Dir_FindFile(file, dirSearchPath); |
2203 | } | | 2203 | } |
2204 | } | | 2204 | } |
2205 | } | | 2205 | } |
2206 | } | | 2206 | } |
2207 | | | 2207 | |
2208 | /* Looking for a system file or file still not found */ | | 2208 | /* Looking for a system file or file still not found */ |
2209 | if (fullname == NULL) { | | 2209 | if (fullname == NULL) { |
2210 | /* | | 2210 | /* |
2211 | * Look for it on the system path | | 2211 | * Look for it on the system path |
2212 | */ | | 2212 | */ |
2213 | SearchPath *path = Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath; | | 2213 | SearchPath *path = Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath; |
2214 | fullname = Dir_FindFile(file, path); | | 2214 | fullname = Dir_FindFile(file, path); |
2215 | } | | 2215 | } |
2216 | | | 2216 | |
2217 | if (fullname == NULL) { | | 2217 | if (fullname == NULL) { |
2218 | if (!silent) | | 2218 | if (!silent) |
2219 | Parse_Error(PARSE_FATAL, "Could not find %s", file); | | 2219 | Parse_Error(PARSE_FATAL, "Could not find %s", file); |
2220 | return; | | 2220 | return; |
2221 | } | | 2221 | } |
2222 | | | 2222 | |
2223 | /* Actually open the file... */ | | 2223 | /* Actually open the file... */ |
2224 | fd = open(fullname, O_RDONLY); | | 2224 | fd = open(fullname, O_RDONLY); |
2225 | if (fd == -1) { | | 2225 | if (fd == -1) { |
2226 | if (!silent) | | 2226 | if (!silent) |
2227 | Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); | | 2227 | Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); |
2228 | free(fullname); | | 2228 | free(fullname); |
2229 | return; | | 2229 | return; |
2230 | } | | 2230 | } |
2231 | | | 2231 | |
2232 | /* load it */ | | 2232 | /* load it */ |
2233 | lf = loadfile(fullname, fd); | | 2233 | lf = loadfile(fullname, fd); |
2234 | | | 2234 | |
2235 | /* Start reading from this file next */ | | 2235 | /* Start reading from this file next */ |
2236 | Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf); | | 2236 | Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf); |
2237 | CurFile()->lf = lf; | | 2237 | CurFile()->lf = lf; |
2238 | if (depinc) | | 2238 | if (depinc) |
2239 | doing_depend = depinc; /* only turn it on */ | | 2239 | doing_depend = depinc; /* only turn it on */ |
2240 | } | | 2240 | } |
2241 | | | 2241 | |
2242 | static void | | 2242 | static void |
2243 | ParseDoInclude(char *line) | | 2243 | ParseDoInclude(char *line) |
2244 | { | | 2244 | { |
2245 | char endc; /* the character which ends the file spec */ | | 2245 | char endc; /* the character which ends the file spec */ |
2246 | char *cp; /* current position in file spec */ | | 2246 | char *cp; /* current position in file spec */ |
2247 | int silent = *line != 'i'; | | 2247 | int silent = *line != 'i'; |
2248 | char *file = line + (silent ? 8 : 7); | | 2248 | char *file = line + (silent ? 8 : 7); |
2249 | | | 2249 | |
2250 | /* Skip to delimiter character so we know where to look */ | | 2250 | /* Skip to delimiter character so we know where to look */ |
2251 | while (*file == ' ' || *file == '\t') | | 2251 | while (*file == ' ' || *file == '\t') |
2252 | file++; | | 2252 | file++; |
2253 | | | 2253 | |
2254 | if (*file != '"' && *file != '<') { | | 2254 | if (*file != '"' && *file != '<') { |
2255 | Parse_Error(PARSE_FATAL, | | 2255 | Parse_Error(PARSE_FATAL, |
2256 | ".include filename must be delimited by '\"' or '<'"); | | 2256 | ".include filename must be delimited by '\"' or '<'"); |
2257 | return; | | 2257 | return; |
2258 | } | | 2258 | } |
2259 | | | 2259 | |
2260 | /* | | 2260 | /* |
2261 | * Set the search path on which to find the include file based on the | | 2261 | * Set the search path on which to find the include file based on the |
2262 | * characters which bracket its name. Angle-brackets imply it's | | 2262 | * characters which bracket its name. Angle-brackets imply it's |
2263 | * a system Makefile while double-quotes imply it's a user makefile | | 2263 | * a system Makefile while double-quotes imply it's a user makefile |
2264 | */ | | 2264 | */ |
2265 | if (*file == '<') { | | 2265 | if (*file == '<') { |
2266 | endc = '>'; | | 2266 | endc = '>'; |
2267 | } else { | | 2267 | } else { |
2268 | endc = '"'; | | 2268 | endc = '"'; |
2269 | } | | 2269 | } |
2270 | | | 2270 | |
2271 | /* Skip to matching delimiter */ | | 2271 | /* Skip to matching delimiter */ |
2272 | for (cp = ++file; *cp && *cp != endc; cp++) | | 2272 | for (cp = ++file; *cp && *cp != endc; cp++) |
2273 | continue; | | 2273 | continue; |
2274 | | | 2274 | |
2275 | if (*cp != endc) { | | 2275 | if (*cp != endc) { |
2276 | Parse_Error(PARSE_FATAL, | | 2276 | Parse_Error(PARSE_FATAL, |
2277 | "Unclosed %cinclude filename. '%c' expected", | | 2277 | "Unclosed %cinclude filename. '%c' expected", |
2278 | '.', endc); | | 2278 | '.', endc); |
2279 | return; | | 2279 | return; |
2280 | } | | 2280 | } |
2281 | *cp = '\0'; | | 2281 | *cp = '\0'; |
2282 | | | 2282 | |
2283 | /* | | 2283 | /* |
2284 | * Substitute for any variables in the file name before trying to | | 2284 | * Substitute for any variables in the file name before trying to |
2285 | * find the thing. | | 2285 | * find the thing. |
2286 | */ | | 2286 | */ |
2287 | (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &file); | | 2287 | (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &file); |
2288 | /* TODO: handle errors */ | | 2288 | /* TODO: handle errors */ |
2289 | | | 2289 | |
2290 | Parse_include_file(file, endc == '>', *line == 'd', silent); | | 2290 | Parse_include_file(file, endc == '>', *line == 'd', silent); |
2291 | free(file); | | 2291 | free(file); |
2292 | } | | 2292 | } |
2293 | | | 2293 | |
2294 | /* Split filename into dirname + basename, then assign these to the | | 2294 | /* Split filename into dirname + basename, then assign these to the |
2295 | * given variables. */ | | 2295 | * given variables. */ |
2296 | static void | | 2296 | static void |
2297 | SetFilenameVars(const char *filename, const char *dirvar, const char *filevar) | | 2297 | SetFilenameVars(const char *filename, const char *dirvar, const char *filevar) |
2298 | { | | 2298 | { |
2299 | const char *slash, *dirname, *basename; | | 2299 | const char *slash, *dirname, *basename; |
2300 | void *freeIt; | | 2300 | void *freeIt; |
2301 | | | 2301 | |
2302 | slash = strrchr(filename, '/'); | | 2302 | slash = strrchr(filename, '/'); |
2303 | if (slash == NULL) { | | 2303 | if (slash == NULL) { |
2304 | dirname = curdir; | | 2304 | dirname = curdir; |
2305 | basename = filename; | | 2305 | basename = filename; |
2306 | freeIt = NULL; | | 2306 | freeIt = NULL; |
2307 | } else { | | 2307 | } else { |
2308 | dirname = freeIt = bmake_strsedup(filename, slash); | | 2308 | dirname = freeIt = bmake_strsedup(filename, slash); |
2309 | basename = slash + 1; | | 2309 | basename = slash + 1; |
2310 | } | | 2310 | } |
2311 | | | 2311 | |
2312 | Var_Set(dirvar, dirname, VAR_GLOBAL); | | 2312 | Var_Set(dirvar, dirname, VAR_GLOBAL); |
2313 | Var_Set(filevar, basename, VAR_GLOBAL); | | 2313 | Var_Set(filevar, basename, VAR_GLOBAL); |
2314 | | | 2314 | |
2315 | DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n", | | 2315 | DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n", |
2316 | __func__, dirvar, dirname, filevar, basename); | | 2316 | __func__, dirvar, dirname, filevar, basename); |
2317 | free(freeIt); | | 2317 | free(freeIt); |
2318 | } | | 2318 | } |
2319 | | | 2319 | |
2320 | /* Return the immediately including file. | | 2320 | /* Return the immediately including file. |
2321 | * | | 2321 | * |
2322 | * This is made complicated since the .for loop is implemented as a special | | 2322 | * This is made complicated since the .for loop is implemented as a special |
2323 | * kind of .include; see For_Run. */ | | 2323 | * kind of .include; see For_Run. */ |
2324 | static const char * | | 2324 | static const char * |
2325 | GetActuallyIncludingFile(void) | | 2325 | GetActuallyIncludingFile(void) |
2326 | { | | 2326 | { |
2327 | size_t i; | | 2327 | size_t i; |
2328 | const IFile *incs = GetInclude(0); | | 2328 | const IFile *incs = GetInclude(0); |
2329 | | | 2329 | |
2330 | for (i = includes.len; i >= 2; i--) | | 2330 | for (i = includes.len; i >= 2; i--) |
2331 | if (!incs[i - 1].fromForLoop) | | 2331 | if (!incs[i - 1].fromForLoop) |
2332 | return incs[i - 2].fname; | | 2332 | return incs[i - 2].fname; |
2333 | return NULL; | | 2333 | return NULL; |
2334 | } | | 2334 | } |
2335 | | | 2335 | |
2336 | /* Set .PARSEDIR, .PARSEFILE, .INCLUDEDFROMDIR and .INCLUDEDFROMFILE. */ | | 2336 | /* Set .PARSEDIR, .PARSEFILE, .INCLUDEDFROMDIR and .INCLUDEDFROMFILE. */ |
2337 | static void | | 2337 | static void |
2338 | ParseSetParseFile(const char *filename) | | 2338 | ParseSetParseFile(const char *filename) |
2339 | { | | 2339 | { |
2340 | const char *including; | | 2340 | const char *including; |
2341 | | | 2341 | |
2342 | SetFilenameVars(filename, ".PARSEDIR", ".PARSEFILE"); | | 2342 | SetFilenameVars(filename, ".PARSEDIR", ".PARSEFILE"); |
2343 | | | 2343 | |
2344 | including = GetActuallyIncludingFile(); | | 2344 | including = GetActuallyIncludingFile(); |
2345 | if (including != NULL) { | | 2345 | if (including != NULL) { |
2346 | SetFilenameVars(including, | | 2346 | SetFilenameVars(including, |
2347 | ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE"); | | 2347 | ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE"); |
2348 | } else { | | 2348 | } else { |
2349 | Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); | | 2349 | Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); |
2350 | Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); | | 2350 | Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); |
2351 | } | | 2351 | } |
2352 | } | | 2352 | } |
2353 | | | 2353 | |
2354 | /* Track the makefiles we read - so makefiles can set dependencies on them. | | 2354 | /* Track the makefiles we read - so makefiles can set dependencies on them. |
2355 | * Avoid adding anything more than once. */ | | 2355 | * Avoid adding anything more than once. */ |
2356 | static void | | 2356 | static void |
2357 | ParseTrackInput(const char *name) | | 2357 | ParseTrackInput(const char *name) |
2358 | { | | 2358 | { |
2359 | void *fp = NULL; | | 2359 | void *fp = NULL; |
2360 | | | 2360 | |
2361 | const char *old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp); | | 2361 | const char *old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp); |
2362 | if (old) { | | 2362 | if (old) { |
2363 | size_t name_len = strlen(name); | | 2363 | size_t name_len = strlen(name); |
2364 | const char *ep = old + strlen(old) - name_len; | | 2364 | const char *ep = old + strlen(old) - name_len; |
2365 | /* does it contain name? */ | | 2365 | /* does it contain name? */ |
2366 | for (; old != NULL; old = strchr(old, ' ')) { | | 2366 | for (; old != NULL; old = strchr(old, ' ')) { |
2367 | if (*old == ' ') | | 2367 | if (*old == ' ') |
2368 | old++; | | 2368 | old++; |
2369 | if (old >= ep) | | 2369 | if (old > ep) |
2370 | break; /* cannot contain name */ | | 2370 | break; /* cannot contain name */ |
2371 | if (memcmp(old, name, name_len) == 0 && | | 2371 | if (memcmp(old, name, name_len) == 0 && |
2372 | (old[name_len] == '\0' || old[name_len] == ' ')) | | 2372 | (old[name_len] == '\0' || old[name_len] == ' ')) |
2373 | goto cleanup; | | 2373 | goto cleanup; |
2374 | } | | 2374 | } |
2375 | } | | 2375 | } |
2376 | Var_Append(MAKE_MAKEFILES, name, VAR_GLOBAL); | | 2376 | Var_Append(MAKE_MAKEFILES, name, VAR_GLOBAL); |
2377 | cleanup: | | 2377 | cleanup: |
2378 | bmake_free(fp); | | 2378 | bmake_free(fp); |
2379 | } | | 2379 | } |
2380 | | | 2380 | |
2381 | | | 2381 | |
2382 | /* Start Parsing from the given source. | | 2382 | /* Start Parsing from the given source. |
2383 | * | | 2383 | * |
2384 | * The given file is added to the includes stack. */ | | 2384 | * The given file is added to the includes stack. */ |
2385 | void | | 2385 | void |
2386 | Parse_SetInput(const char *name, int line, int fd, | | 2386 | Parse_SetInput(const char *name, int line, int fd, |
2387 | char *(*nextbuf)(void *, size_t *), void *arg) | | 2387 | char *(*nextbuf)(void *, size_t *), void *arg) |
2388 | { | | 2388 | { |
2389 | IFile *curFile; | | 2389 | IFile *curFile; |
2390 | char *buf; | | 2390 | char *buf; |
2391 | size_t len; | | 2391 | size_t len; |
2392 | Boolean fromForLoop = name == NULL; | | 2392 | Boolean fromForLoop = name == NULL; |
2393 | | | 2393 | |
2394 | if (fromForLoop) | | 2394 | if (fromForLoop) |
2395 | name = CurFile()->fname; | | 2395 | name = CurFile()->fname; |
2396 | else | | 2396 | else |
2397 | ParseTrackInput(name); | | 2397 | ParseTrackInput(name); |
2398 | | | 2398 | |
2399 | if (DEBUG(PARSE)) | | 2399 | if (DEBUG(PARSE)) |
2400 | debug_printf("%s: file %s, line %d, fd %d, nextbuf %s, arg %p\n", | | 2400 | debug_printf("%s: file %s, line %d, fd %d, nextbuf %s, arg %p\n", |
2401 | __func__, name, line, fd, | | 2401 | __func__, name, line, fd, |
2402 | nextbuf == loadedfile_nextbuf ? "loadedfile" : "other", | | 2402 | nextbuf == loadedfile_nextbuf ? "loadedfile" : "other", |
2403 | arg); | | 2403 | arg); |
2404 | | | 2404 | |
2405 | if (fd == -1 && nextbuf == NULL) | | 2405 | if (fd == -1 && nextbuf == NULL) |
2406 | /* sanity */ | | 2406 | /* sanity */ |
2407 | return; | | 2407 | return; |
2408 | | | 2408 | |
2409 | curFile = Vector_Push(&includes); | | 2409 | curFile = Vector_Push(&includes); |
2410 | | | 2410 | |
2411 | /* | | 2411 | /* |
2412 | * Once the previous state has been saved, we can get down to reading | | 2412 | * Once the previous state has been saved, we can get down to reading |
2413 | * the new file. We set up the name of the file to be the absolute | | 2413 | * the new file. We set up the name of the file to be the absolute |
2414 | * name of the include file so error messages refer to the right | | 2414 | * name of the include file so error messages refer to the right |
2415 | * place. | | 2415 | * place. |
2416 | */ | | 2416 | */ |
2417 | curFile->fname = bmake_strdup(name); | | 2417 | curFile->fname = bmake_strdup(name); |
2418 | curFile->fromForLoop = fromForLoop; | | 2418 | curFile->fromForLoop = fromForLoop; |
2419 | curFile->lineno = line; | | 2419 | curFile->lineno = line; |
2420 | curFile->first_lineno = line; | | 2420 | curFile->first_lineno = line; |
2421 | curFile->nextbuf = nextbuf; | | 2421 | curFile->nextbuf = nextbuf; |
2422 | curFile->nextbuf_arg = arg; | | 2422 | curFile->nextbuf_arg = arg; |
2423 | curFile->lf = NULL; | | 2423 | curFile->lf = NULL; |
2424 | curFile->depending = doing_depend; /* restore this on EOF */ | | 2424 | curFile->depending = doing_depend; /* restore this on EOF */ |
2425 | | | 2425 | |
2426 | assert(nextbuf != NULL); | | 2426 | assert(nextbuf != NULL); |
2427 | | | 2427 | |
2428 | /* Get first block of input data */ | | 2428 | /* Get first block of input data */ |
2429 | buf = curFile->nextbuf(curFile->nextbuf_arg, &len); | | 2429 | buf = curFile->nextbuf(curFile->nextbuf_arg, &len); |
2430 | if (buf == NULL) { | | 2430 | if (buf == NULL) { |
2431 | /* Was all a waste of time ... */ | | 2431 | /* Was all a waste of time ... */ |
2432 | if (curFile->fname) | | 2432 | if (curFile->fname) |
2433 | free(curFile->fname); | | 2433 | free(curFile->fname); |
2434 | free(curFile); | | 2434 | free(curFile); |
2435 | return; | | 2435 | return; |
2436 | } | | 2436 | } |
2437 | curFile->buf_freeIt = buf; | | 2437 | curFile->buf_freeIt = buf; |
2438 | curFile->buf_ptr = buf; | | 2438 | curFile->buf_ptr = buf; |
2439 | curFile->buf_end = buf + len; | | 2439 | curFile->buf_end = buf + len; |
2440 | | | 2440 | |
2441 | curFile->cond_depth = Cond_save_depth(); | | 2441 | curFile->cond_depth = Cond_save_depth(); |
2442 | ParseSetParseFile(name); | | 2442 | ParseSetParseFile(name); |
2443 | } | | 2443 | } |
2444 | | | 2444 | |
2445 | /* Check if the directive is an include directive. */ | | 2445 | /* Check if the directive is an include directive. */ |
2446 | static Boolean | | 2446 | static Boolean |
2447 | IsInclude(const char *dir, Boolean sysv) | | 2447 | IsInclude(const char *dir, Boolean sysv) |
2448 | { | | 2448 | { |
2449 | if (dir[0] == 's' || dir[0] == '-' || (dir[0] == 'd' && !sysv)) | | 2449 | if (dir[0] == 's' || dir[0] == '-' || (dir[0] == 'd' && !sysv)) |
2450 | dir++; | | 2450 | dir++; |
2451 | | | 2451 | |
2452 | if (strncmp(dir, "include", 7) != 0) | | 2452 | if (strncmp(dir, "include", 7) != 0) |
2453 | return FALSE; | | 2453 | return FALSE; |
2454 | | | 2454 | |
2455 | /* Space is not mandatory for BSD .include */ | | 2455 | /* Space is not mandatory for BSD .include */ |
2456 | return !sysv || ch_isspace(dir[7]); | | 2456 | return !sysv || ch_isspace(dir[7]); |
2457 | } | | 2457 | } |
2458 | | | 2458 | |
2459 | | | 2459 | |
2460 | #ifdef SYSVINCLUDE | | 2460 | #ifdef SYSVINCLUDE |
2461 | /* Check if the line is a SYSV include directive. */ | | 2461 | /* Check if the line is a SYSV include directive. */ |
2462 | static Boolean | | 2462 | static Boolean |
2463 | IsSysVInclude(const char *line) | | 2463 | IsSysVInclude(const char *line) |
2464 | { | | 2464 | { |
2465 | const char *p; | | 2465 | const char *p; |
2466 | | | 2466 | |
2467 | if (!IsInclude(line, TRUE)) | | 2467 | if (!IsInclude(line, TRUE)) |
2468 | return FALSE; | | 2468 | return FALSE; |
2469 | | | 2469 | |
2470 | /* Avoid interpreting a dependency line as an include */ | | 2470 | /* Avoid interpreting a dependency line as an include */ |
2471 | for (p = line; (p = strchr(p, ':')) != NULL;) { | | 2471 | for (p = line; (p = strchr(p, ':')) != NULL;) { |
2472 | if (*++p == '\0') { | | 2472 | if (*++p == '\0') { |
2473 | /* end of line -> dependency */ | | 2473 | /* end of line -> dependency */ |
2474 | return FALSE; | | 2474 | return FALSE; |
2475 | } | | 2475 | } |
2476 | if (*p == ':' || ch_isspace(*p)) { | | 2476 | if (*p == ':' || ch_isspace(*p)) { |
2477 | /* :: operator or ': ' -> dependency */ | | 2477 | /* :: operator or ': ' -> dependency */ |
2478 | return FALSE; | | 2478 | return FALSE; |
2479 | } | | 2479 | } |
2480 | } | | 2480 | } |
2481 | return TRUE; | | 2481 | return TRUE; |
2482 | } | | 2482 | } |
2483 | | | 2483 | |
2484 | /* Push to another file. The line points to the word "include". */ | | 2484 | /* Push to another file. The line points to the word "include". */ |
2485 | static void | | 2485 | static void |
2486 | ParseTraditionalInclude(char *line) | | 2486 | ParseTraditionalInclude(char *line) |
2487 | { | | 2487 | { |
2488 | char *cp; /* current position in file spec */ | | 2488 | char *cp; /* current position in file spec */ |
2489 | int done = 0; | | 2489 | int done = 0; |
2490 | int silent = line[0] != 'i'; | | 2490 | int silent = line[0] != 'i'; |
2491 | char *file = line + (silent ? 8 : 7); | | 2491 | char *file = line + (silent ? 8 : 7); |
2492 | char *all_files; | | 2492 | char *all_files; |
2493 | | | 2493 | |
2494 | DEBUG2(PARSE, "%s: %s\n", __func__, file); | | 2494 | DEBUG2(PARSE, "%s: %s\n", __func__, file); |
2495 | | | 2495 | |
2496 | pp_skip_whitespace(&file); | | 2496 | pp_skip_whitespace(&file); |
2497 | | | 2497 | |
2498 | /* | | 2498 | /* |
2499 | * Substitute for any variables in the file name before trying to | | 2499 | * Substitute for any variables in the file name before trying to |
2500 | * find the thing. | | 2500 | * find the thing. |
2501 | */ | | 2501 | */ |
2502 | (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &all_files); | | 2502 | (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &all_files); |
2503 | /* TODO: handle errors */ | | 2503 | /* TODO: handle errors */ |
2504 | | | 2504 | |
2505 | if (*file == '\0') { | | 2505 | if (*file == '\0') { |
2506 | Parse_Error(PARSE_FATAL, "Filename missing from \"include\""); | | 2506 | Parse_Error(PARSE_FATAL, "Filename missing from \"include\""); |
2507 | goto out; | | 2507 | goto out; |
2508 | } | | 2508 | } |
2509 | | | 2509 | |
2510 | for (file = all_files; !done; file = cp + 1) { | | 2510 | for (file = all_files; !done; file = cp + 1) { |
2511 | /* Skip to end of line or next whitespace */ | | 2511 | /* Skip to end of line or next whitespace */ |
2512 | for (cp = file; *cp && !ch_isspace(*cp); cp++) | | 2512 | for (cp = file; *cp && !ch_isspace(*cp); cp++) |
2513 | continue; | | 2513 | continue; |
2514 | | | 2514 | |
2515 | if (*cp) | | 2515 | if (*cp) |
2516 | *cp = '\0'; | | 2516 | *cp = '\0'; |
2517 | else | | 2517 | else |
2518 | done = 1; | | 2518 | done = 1; |
2519 | | | 2519 | |
2520 | Parse_include_file(file, FALSE, FALSE, silent); | | 2520 | Parse_include_file(file, FALSE, FALSE, silent); |
2521 | } | | 2521 | } |
2522 | out: | | 2522 | out: |
2523 | free(all_files); | | 2523 | free(all_files); |
2524 | } | | 2524 | } |
2525 | #endif | | 2525 | #endif |
2526 | | | 2526 | |
2527 | #ifdef GMAKEEXPORT | | 2527 | #ifdef GMAKEEXPORT |
2528 | /* Parse "export <variable>=<value>", and actually export it. */ | | 2528 | /* Parse "export <variable>=<value>", and actually export it. */ |
2529 | static void | | 2529 | static void |
2530 | ParseGmakeExport(char *line) | | 2530 | ParseGmakeExport(char *line) |
2531 | { | | 2531 | { |
2532 | char *variable = line + 6; | | 2532 | char *variable = line + 6; |
2533 | char *value; | | 2533 | char *value; |
2534 | | | 2534 | |
2535 | DEBUG2(PARSE, "%s: %s\n", __func__, variable); | | 2535 | DEBUG2(PARSE, "%s: %s\n", __func__, variable); |
2536 | | | 2536 | |
2537 | pp_skip_whitespace(&variable); | | 2537 | pp_skip_whitespace(&variable); |
2538 | | | 2538 | |
2539 | for (value = variable; *value && *value != '='; value++) | | 2539 | for (value = variable; *value && *value != '='; value++) |
2540 | continue; | | 2540 | continue; |
2541 | | | 2541 | |
2542 | if (*value != '=') { | | 2542 | if (*value != '=') { |
2543 | Parse_Error(PARSE_FATAL, | | 2543 | Parse_Error(PARSE_FATAL, |
2544 | "Variable/Value missing from \"export\""); | | 2544 | "Variable/Value missing from \"export\""); |
2545 | return; | | 2545 | return; |
2546 | } | | 2546 | } |
2547 | *value++ = '\0'; /* terminate variable */ | | 2547 | *value++ = '\0'; /* terminate variable */ |
2548 | | | 2548 | |
2549 | /* | | 2549 | /* |
2550 | * Expand the value before putting it in the environment. | | 2550 | * Expand the value before putting it in the environment. |
2551 | */ | | 2551 | */ |
2552 | (void)Var_Subst(value, VAR_CMDLINE, VARE_WANTRES, &value); | | 2552 | (void)Var_Subst(value, VAR_CMDLINE, VARE_WANTRES, &value); |
2553 | /* TODO: handle errors */ | | 2553 | /* TODO: handle errors */ |
2554 | | | 2554 | |
2555 | setenv(variable, value, 1); | | 2555 | setenv(variable, value, 1); |
2556 | free(value); | | 2556 | free(value); |
2557 | } | | 2557 | } |
2558 | #endif | | 2558 | #endif |
2559 | | | 2559 | |
2560 | /* Called when EOF is reached in the current file. If we were reading an | | 2560 | /* Called when EOF is reached in the current file. If we were reading an |
2561 | * include file, the includes stack is popped and things set up to go back | | 2561 | * include file, the includes stack is popped and things set up to go back |
2562 | * to reading the previous file at the previous location. | | 2562 | * to reading the previous file at the previous location. |
2563 | * | | 2563 | * |
2564 | * Results: | | 2564 | * Results: |
2565 | * TRUE to continue parsing, i.e. it had only reached the end of an | | 2565 | * TRUE to continue parsing, i.e. it had only reached the end of an |
2566 | * included file, FALSE if the main file has been parsed completely. | | 2566 | * included file, FALSE if the main file has been parsed completely. |
2567 | */ | | 2567 | */ |
2568 | static Boolean | | 2568 | static Boolean |
2569 | ParseEOF(void) | | 2569 | ParseEOF(void) |
2570 | { | | 2570 | { |
2571 | char *ptr; | | 2571 | char *ptr; |
2572 | size_t len; | | 2572 | size_t len; |
2573 | IFile *curFile = CurFile(); | | 2573 | IFile *curFile = CurFile(); |
2574 | | | 2574 | |
2575 | assert(curFile->nextbuf != NULL); | | 2575 | assert(curFile->nextbuf != NULL); |
2576 | | | 2576 | |
2577 | doing_depend = curFile->depending; /* restore this */ | | 2577 | doing_depend = curFile->depending; /* restore this */ |
2578 | /* get next input buffer, if any */ | | 2578 | /* get next input buffer, if any */ |
2579 | ptr = curFile->nextbuf(curFile->nextbuf_arg, &len); | | 2579 | ptr = curFile->nextbuf(curFile->nextbuf_arg, &len); |
2580 | curFile->buf_ptr = ptr; | | 2580 | curFile->buf_ptr = ptr; |
2581 | curFile->buf_freeIt = ptr; | | 2581 | curFile->buf_freeIt = ptr; |
2582 | curFile->buf_end = ptr + len; | | 2582 | curFile->buf_end = ptr + len; |
2583 | curFile->lineno = curFile->first_lineno; | | 2583 | curFile->lineno = curFile->first_lineno; |
2584 | if (ptr != NULL) { | | 2584 | if (ptr != NULL) { |
2585 | /* Iterate again */ | | 2585 | /* Iterate again */ |
2586 | return TRUE; | | 2586 | return TRUE; |
2587 | } | | 2587 | } |
2588 | | | 2588 | |
2589 | /* Ensure the makefile (or loop) didn't have mismatched conditionals */ | | 2589 | /* Ensure the makefile (or loop) didn't have mismatched conditionals */ |
2590 | Cond_restore_depth(curFile->cond_depth); | | 2590 | Cond_restore_depth(curFile->cond_depth); |
2591 | | | 2591 | |
2592 | if (curFile->lf != NULL) { | | 2592 | if (curFile->lf != NULL) { |
2593 | loadedfile_destroy(curFile->lf); | | 2593 | loadedfile_destroy(curFile->lf); |
2594 | curFile->lf = NULL; | | 2594 | curFile->lf = NULL; |
2595 | } | | 2595 | } |
2596 | | | 2596 | |
2597 | /* Dispose of curFile info */ | | 2597 | /* Dispose of curFile info */ |
2598 | /* Leak curFile->fname because all the gnodes have pointers to it */ | | 2598 | /* Leak curFile->fname because all the gnodes have pointers to it */ |
2599 | free(curFile->buf_freeIt); | | 2599 | free(curFile->buf_freeIt); |
2600 | Vector_Pop(&includes); | | 2600 | Vector_Pop(&includes); |
2601 | | | 2601 | |
2602 | if (includes.len == 0) { | | 2602 | if (includes.len == 0) { |
2603 | /* We've run out of input */ | | 2603 | /* We've run out of input */ |
2604 | Var_Delete(".PARSEDIR", VAR_GLOBAL); | | 2604 | Var_Delete(".PARSEDIR", VAR_GLOBAL); |
2605 | Var_Delete(".PARSEFILE", VAR_GLOBAL); | | 2605 | Var_Delete(".PARSEFILE", VAR_GLOBAL); |
2606 | Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); | | 2606 | Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); |
2607 | Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); | | 2607 | Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); |
2608 | return FALSE; | | 2608 | return FALSE; |
2609 | } | | 2609 | } |
2610 | | | 2610 | |
2611 | curFile = CurFile(); | | 2611 | curFile = CurFile(); |
2612 | DEBUG2(PARSE, "ParseEOF: returning to file %s, line %d\n", | | 2612 | DEBUG2(PARSE, "ParseEOF: returning to file %s, line %d\n", |
2613 | curFile->fname, curFile->lineno); | | 2613 | curFile->fname, curFile->lineno); |
2614 | | | 2614 | |
2615 | ParseSetParseFile(curFile->fname); | | 2615 | ParseSetParseFile(curFile->fname); |
2616 | return TRUE; | | 2616 | return TRUE; |
2617 | } | | 2617 | } |
2618 | | | 2618 | |
2619 | #define PARSE_RAW 1 | | 2619 | #define PARSE_RAW 1 |
2620 | #define PARSE_SKIP 2 | | 2620 | #define PARSE_SKIP 2 |
2621 | | | 2621 | |
2622 | static char * | | 2622 | static char * |
2623 | ParseGetLine(int flags) | | 2623 | ParseGetLine(int flags) |
2624 | { | | 2624 | { |
2625 | IFile *cf = CurFile(); | | 2625 | IFile *cf = CurFile(); |
2626 | char *ptr; | | 2626 | char *ptr; |
2627 | char ch; | | 2627 | char ch; |
2628 | char *line; | | 2628 | char *line; |
2629 | char *line_end; | | 2629 | char *line_end; |
2630 | char *escaped; | | 2630 | char *escaped; |
2631 | char *comment; | | 2631 | char *comment; |
2632 | char *tp; | | 2632 | char *tp; |
2633 | | | 2633 | |
2634 | /* Loop through blank lines and comment lines */ | | 2634 | /* Loop through blank lines and comment lines */ |
2635 | for (;;) { | | 2635 | for (;;) { |
2636 | cf->lineno++; | | 2636 | cf->lineno++; |
2637 | line = cf->buf_ptr; | | 2637 | line = cf->buf_ptr; |
2638 | ptr = line; | | 2638 | ptr = line; |
2639 | line_end = line; | | 2639 | line_end = line; |
2640 | escaped = NULL; | | 2640 | escaped = NULL; |
2641 | comment = NULL; | | 2641 | comment = NULL; |
2642 | for (;;) { | | 2642 | for (;;) { |
2643 | /* XXX: can buf_end ever be null? */ | | 2643 | /* XXX: can buf_end ever be null? */ |
2644 | if (cf->buf_end != NULL && ptr == cf->buf_end) { | | 2644 | if (cf->buf_end != NULL && ptr == cf->buf_end) { |
2645 | /* end of buffer */ | | 2645 | /* end of buffer */ |
2646 | ch = 0; | | 2646 | ch = 0; |
2647 | break; | | 2647 | break; |
2648 | } | | 2648 | } |
2649 | ch = *ptr; | | 2649 | ch = *ptr; |
2650 | if (ch == 0 || (ch == '\\' && ptr[1] == 0)) { | | 2650 | if (ch == 0 || (ch == '\\' && ptr[1] == 0)) { |
2651 | /* XXX: can buf_end ever be null? */ | | 2651 | /* XXX: can buf_end ever be null? */ |
2652 | if (cf->buf_end == NULL) | | 2652 | if (cf->buf_end == NULL) |
2653 | /* End of string (aka for loop) data */ | | 2653 | /* End of string (aka for loop) data */ |
2654 | break; | | 2654 | break; |
2655 | /* see if there is more we can parse */ | | 2655 | /* see if there is more we can parse */ |
2656 | while (ptr++ < cf->buf_end) { | | 2656 | while (ptr++ < cf->buf_end) { |
2657 | if ((ch = *ptr) == '\n') { | | 2657 | if ((ch = *ptr) == '\n') { |
2658 | if (ptr > line && ptr[-1] == '\\') | | 2658 | if (ptr > line && ptr[-1] == '\\') |
2659 | continue; | | 2659 | continue; |
2660 | Parse_Error(PARSE_WARNING, | | 2660 | Parse_Error(PARSE_WARNING, |
2661 | "Zero byte read from file, " | | 2661 | "Zero byte read from file, " |
2662 | "skipping rest of line."); | | 2662 | "skipping rest of line."); |
2663 | break; | | 2663 | break; |
2664 | } | | 2664 | } |
2665 | } | | 2665 | } |
2666 | if (cf->nextbuf != NULL) { | | 2666 | if (cf->nextbuf != NULL) { |
2667 | /* | | 2667 | /* |
2668 | * End of this buffer; return EOF and outer logic | | 2668 | * End of this buffer; return EOF and outer logic |
2669 | * will get the next one. (eww) | | 2669 | * will get the next one. (eww) |
2670 | */ | | 2670 | */ |
2671 | break; | | 2671 | break; |
2672 | } | | 2672 | } |
2673 | Parse_Error(PARSE_FATAL, "Zero byte read from file"); | | 2673 | Parse_Error(PARSE_FATAL, "Zero byte read from file"); |
2674 | return NULL; | | 2674 | return NULL; |
2675 | } | | 2675 | } |
2676 | | | 2676 | |
2677 | if (ch == '\\') { | | 2677 | if (ch == '\\') { |
2678 | /* Don't treat next character as special, remember first one */ | | 2678 | /* Don't treat next character as special, remember first one */ |
2679 | if (escaped == NULL) | | 2679 | if (escaped == NULL) |
2680 | escaped = ptr; | | 2680 | escaped = ptr; |
2681 | if (ptr[1] == '\n') | | 2681 | if (ptr[1] == '\n') |
2682 | cf->lineno++; | | 2682 | cf->lineno++; |
2683 | ptr += 2; | | 2683 | ptr += 2; |
2684 | line_end = ptr; | | 2684 | line_end = ptr; |
2685 | continue; | | 2685 | continue; |
2686 | } | | 2686 | } |
2687 | if (ch == '#' && comment == NULL) { | | 2687 | if (ch == '#' && comment == NULL) { |
2688 | /* Remember first '#' for comment stripping */ | | 2688 | /* Remember first '#' for comment stripping */ |
2689 | /* Unless previous char was '[', as in modifier :[#] */ | | 2689 | /* Unless previous char was '[', as in modifier :[#] */ |
2690 | if (!(ptr > line && ptr[-1] == '[')) | | 2690 | if (!(ptr > line && ptr[-1] == '[')) |
2691 | comment = line_end; | | 2691 | comment = line_end; |
2692 | } | | 2692 | } |
2693 | ptr++; | | 2693 | ptr++; |
2694 | if (ch == '\n') | | 2694 | if (ch == '\n') |
2695 | break; | | 2695 | break; |
2696 | if (!ch_isspace(ch)) | | 2696 | if (!ch_isspace(ch)) |
2697 | /* We are not interested in trailing whitespace */ | | 2697 | /* We are not interested in trailing whitespace */ |
2698 | line_end = ptr; | | 2698 | line_end = ptr; |
2699 | } | | 2699 | } |
2700 | | | 2700 | |
2701 | /* Save next 'to be processed' location */ | | 2701 | /* Save next 'to be processed' location */ |
2702 | cf->buf_ptr = ptr; | | 2702 | cf->buf_ptr = ptr; |
2703 | | | 2703 | |
2704 | /* Check we have a non-comment, non-blank line */ | | 2704 | /* Check we have a non-comment, non-blank line */ |
2705 | if (line_end == line || comment == line) { | | 2705 | if (line_end == line || comment == line) { |
2706 | if (ch == 0) | | 2706 | if (ch == 0) |
2707 | /* At end of file */ | | 2707 | /* At end of file */ |
2708 | return NULL; | | 2708 | return NULL; |
2709 | /* Parse another line */ | | 2709 | /* Parse another line */ |
2710 | continue; | | 2710 | continue; |
2711 | } | | 2711 | } |
2712 | | | 2712 | |
2713 | /* We now have a line of data */ | | 2713 | /* We now have a line of data */ |
2714 | *line_end = 0; | | 2714 | *line_end = 0; |
2715 | | | 2715 | |
2716 | if (flags & PARSE_RAW) { | | 2716 | if (flags & PARSE_RAW) { |
2717 | /* Leave '\' (etc) in line buffer (eg 'for' lines) */ | | 2717 | /* Leave '\' (etc) in line buffer (eg 'for' lines) */ |
2718 | return line; | | 2718 | return line; |
2719 | } | | 2719 | } |
2720 | | | 2720 | |
2721 | if (flags & PARSE_SKIP) { | | 2721 | if (flags & PARSE_SKIP) { |
2722 | /* Completely ignore non-directives */ | | 2722 | /* Completely ignore non-directives */ |
2723 | if (line[0] != '.') | | 2723 | if (line[0] != '.') |
2724 | continue; | | 2724 | continue; |
2725 | /* We could do more of the .else/.elif/.endif checks here */ | | 2725 | /* We could do more of the .else/.elif/.endif checks here */ |
2726 | } | | 2726 | } |
2727 | break; | | 2727 | break; |
2728 | } | | 2728 | } |
2729 | | | 2729 | |
2730 | /* Brutally ignore anything after a non-escaped '#' in non-commands */ | | 2730 | /* Brutally ignore anything after a non-escaped '#' in non-commands */ |
2731 | if (comment != NULL && line[0] != '\t') { | | 2731 | if (comment != NULL && line[0] != '\t') { |
2732 | line_end = comment; | | 2732 | line_end = comment; |
2733 | *line_end = 0; | | 2733 | *line_end = 0; |
2734 | } | | 2734 | } |
2735 | | | 2735 | |
2736 | /* If we didn't see a '\\' then the in-situ data is fine */ | | 2736 | /* If we didn't see a '\\' then the in-situ data is fine */ |
2737 | if (escaped == NULL) | | 2737 | if (escaped == NULL) |
2738 | return line; | | 2738 | return line; |
2739 | | | 2739 | |
2740 | /* Remove escapes from '\n' and '#' */ | | 2740 | /* Remove escapes from '\n' and '#' */ |
2741 | tp = ptr = escaped; | | 2741 | tp = ptr = escaped; |
2742 | escaped = line; | | 2742 | escaped = line; |
2743 | for (; ; *tp++ = ch) { | | 2743 | for (; ; *tp++ = ch) { |
2744 | ch = *ptr++; | | 2744 | ch = *ptr++; |
2745 | if (ch != '\\') { | | 2745 | if (ch != '\\') { |
2746 | if (ch == 0) | | 2746 | if (ch == 0) |
2747 | break; | | 2747 | break; |
2748 | continue; | | 2748 | continue; |
2749 | } | | 2749 | } |
2750 | | | 2750 | |
2751 | ch = *ptr++; | | 2751 | ch = *ptr++; |
2752 | if (ch == 0) { | | 2752 | if (ch == 0) { |
2753 | /* Delete '\\' at end of buffer */ | | 2753 | /* Delete '\\' at end of buffer */ |
2754 | tp--; | | 2754 | tp--; |
2755 | break; | | 2755 | break; |
2756 | } | | 2756 | } |
2757 | | | 2757 | |
2758 | if (ch == '#' && line[0] != '\t') | | 2758 | if (ch == '#' && line[0] != '\t') |
2759 | /* Delete '\\' from before '#' on non-command lines */ | | 2759 | /* Delete '\\' from before '#' on non-command lines */ |
2760 | continue; | | 2760 | continue; |
2761 | | | 2761 | |
2762 | if (ch != '\n') { | | 2762 | if (ch != '\n') { |
2763 | /* Leave '\\' in buffer for later */ | | 2763 | /* Leave '\\' in buffer for later */ |
2764 | *tp++ = '\\'; | | 2764 | *tp++ = '\\'; |
2765 | /* Make sure we don't delete an escaped ' ' from the line end */ | | 2765 | /* Make sure we don't delete an escaped ' ' from the line end */ |
2766 | escaped = tp + 1; | | 2766 | escaped = tp + 1; |
2767 | continue; | | 2767 | continue; |
2768 | } | | 2768 | } |
2769 | | | 2769 | |
2770 | /* Escaped '\n' replace following whitespace with a single ' ' */ | | 2770 | /* Escaped '\n' replace following whitespace with a single ' ' */ |
2771 | while (ptr[0] == ' ' || ptr[0] == '\t') | | 2771 | while (ptr[0] == ' ' || ptr[0] == '\t') |
2772 | ptr++; | | 2772 | ptr++; |
2773 | ch = ' '; | | 2773 | ch = ' '; |
2774 | } | | 2774 | } |
2775 | | | 2775 | |
2776 | /* Delete any trailing spaces - eg from empty continuations */ | | 2776 | /* Delete any trailing spaces - eg from empty continuations */ |
2777 | while (tp > escaped && ch_isspace(tp[-1])) | | 2777 | while (tp > escaped && ch_isspace(tp[-1])) |
2778 | tp--; | | 2778 | tp--; |
2779 | | | 2779 | |
2780 | *tp = 0; | | 2780 | *tp = 0; |
2781 | return line; | | 2781 | return line; |
2782 | } | | 2782 | } |
2783 | | | 2783 | |
2784 | /* Read an entire line from the input file. Called only by Parse_File. | | 2784 | /* Read an entire line from the input file. Called only by Parse_File. |
2785 | * | | 2785 | * |
2786 | * Results: | | 2786 | * Results: |
2787 | * A line without its newline. | | 2787 | * A line without its newline. |
2788 | * | | 2788 | * |
2789 | * Side Effects: | | 2789 | * Side Effects: |
2790 | * Only those associated with reading a character | | 2790 | * Only those associated with reading a character |
2791 | */ | | 2791 | */ |
2792 | static char * | | 2792 | static char * |
2793 | ParseReadLine(void) | | 2793 | ParseReadLine(void) |
2794 | { | | 2794 | { |
2795 | char *line; /* Result */ | | 2795 | char *line; /* Result */ |
2796 | int lineno; /* Saved line # */ | | 2796 | int lineno; /* Saved line # */ |
2797 | int rval; | | 2797 | int rval; |
2798 | | | 2798 | |
2799 | for (;;) { | | 2799 | for (;;) { |
2800 | line = ParseGetLine(0); | | 2800 | line = ParseGetLine(0); |
2801 | if (line == NULL) | | 2801 | if (line == NULL) |
2802 | return NULL; | | 2802 | return NULL; |
2803 | | | 2803 | |
2804 | if (line[0] != '.') | | 2804 | if (line[0] != '.') |
2805 | return line; | | 2805 | return line; |
2806 | | | 2806 | |
2807 | /* | | 2807 | /* |
2808 | * The line might be a conditional. Ask the conditional module | | 2808 | * The line might be a conditional. Ask the conditional module |
2809 | * about it and act accordingly | | 2809 | * about it and act accordingly |
2810 | */ | | 2810 | */ |
2811 | switch (Cond_EvalLine(line)) { | | 2811 | switch (Cond_EvalLine(line)) { |
2812 | case COND_SKIP: | | 2812 | case COND_SKIP: |
2813 | /* Skip to next conditional that evaluates to COND_PARSE. */ | | 2813 | /* Skip to next conditional that evaluates to COND_PARSE. */ |
2814 | do { | | 2814 | do { |
2815 | line = ParseGetLine(PARSE_SKIP); | | 2815 | line = ParseGetLine(PARSE_SKIP); |
2816 | } while (line && Cond_EvalLine(line) != COND_PARSE); | | 2816 | } while (line && Cond_EvalLine(line) != COND_PARSE); |
2817 | if (line == NULL) | | 2817 | if (line == NULL) |
2818 | break; | | 2818 | break; |
2819 | continue; | | 2819 | continue; |
2820 | case COND_PARSE: | | 2820 | case COND_PARSE: |
2821 | continue; | | 2821 | continue; |
2822 | case COND_INVALID: /* Not a conditional line */ | | 2822 | case COND_INVALID: /* Not a conditional line */ |
2823 | /* Check for .for loops */ | | 2823 | /* Check for .for loops */ |
2824 | rval = For_Eval(line); | | 2824 | rval = For_Eval(line); |
2825 | if (rval == 0) | | 2825 | if (rval == 0) |
2826 | /* Not a .for line */ | | 2826 | /* Not a .for line */ |
2827 | break; | | 2827 | break; |
2828 | if (rval < 0) | | 2828 | if (rval < 0) |
2829 | /* Syntax error - error printed, ignore line */ | | 2829 | /* Syntax error - error printed, ignore line */ |
2830 | continue; | | 2830 | continue; |
2831 | /* Start of a .for loop */ | | 2831 | /* Start of a .for loop */ |
2832 | lineno = CurFile()->lineno; | | 2832 | lineno = CurFile()->lineno; |
2833 | /* Accumulate loop lines until matching .endfor */ | | 2833 | /* Accumulate loop lines until matching .endfor */ |
2834 | do { | | 2834 | do { |
2835 | line = ParseGetLine(PARSE_RAW); | | 2835 | line = ParseGetLine(PARSE_RAW); |
2836 | if (line == NULL) { | | 2836 | if (line == NULL) { |
2837 | Parse_Error(PARSE_FATAL, | | 2837 | Parse_Error(PARSE_FATAL, |
2838 | "Unexpected end of file in for loop."); | | 2838 | "Unexpected end of file in for loop."); |
2839 | break; | | 2839 | break; |
2840 | } | | 2840 | } |
2841 | } while (For_Accum(line)); | | 2841 | } while (For_Accum(line)); |
2842 | /* Stash each iteration as a new 'input file' */ | | 2842 | /* Stash each iteration as a new 'input file' */ |
2843 | For_Run(lineno); | | 2843 | For_Run(lineno); |
2844 | /* Read next line from for-loop buffer */ | | 2844 | /* Read next line from for-loop buffer */ |
2845 | continue; | | 2845 | continue; |
2846 | } | | 2846 | } |
2847 | return line; | | 2847 | return line; |
2848 | } | | 2848 | } |
2849 | } | | 2849 | } |
2850 | | | 2850 | |
2851 | static void | | 2851 | static void |
2852 | FinishDependencyGroup(void) | | 2852 | FinishDependencyGroup(void) |
2853 | { | | 2853 | { |
2854 | if (targets != NULL) { | | 2854 | if (targets != NULL) { |
2855 | GNodeListNode *ln; | | 2855 | GNodeListNode *ln; |
2856 | for (ln = targets->first; ln != NULL; ln = ln->next) { | | 2856 | for (ln = targets->first; ln != NULL; ln = ln->next) { |
2857 | GNode *gn = ln->datum; | | 2857 | GNode *gn = ln->datum; |
2858 | | | 2858 | |
2859 | Suff_EndTransform(gn); | | 2859 | Suff_EndTransform(gn); |
2860 | | | 2860 | |
2861 | /* Mark the target as already having commands if it does, to | | 2861 | /* Mark the target as already having commands if it does, to |
2862 | * keep from having shell commands on multiple dependency lines. */ | | 2862 | * keep from having shell commands on multiple dependency lines. */ |
2863 | if (!Lst_IsEmpty(gn->commands)) | | 2863 | if (!Lst_IsEmpty(gn->commands)) |
2864 | gn->type |= OP_HAS_COMMANDS; | | 2864 | gn->type |= OP_HAS_COMMANDS; |
2865 | } | | 2865 | } |
2866 | | | 2866 | |
2867 | Lst_Free(targets); | | 2867 | Lst_Free(targets); |
2868 | targets = NULL; | | 2868 | targets = NULL; |
2869 | } | | 2869 | } |
2870 | } | | 2870 | } |
2871 | | | 2871 | |
2872 | /* Add the command to each target from the current dependency spec. */ | | 2872 | /* Add the command to each target from the current dependency spec. */ |
2873 | static void | | 2873 | static void |
2874 | ParseLine_ShellCommand(const char *p) | | 2874 | ParseLine_ShellCommand(const char *p) |
2875 | { | | 2875 | { |
2876 | cpp_skip_whitespace(&p); | | 2876 | cpp_skip_whitespace(&p); |
2877 | if (*p == '\0') | | 2877 | if (*p == '\0') |
2878 | return; /* skip empty commands */ | | 2878 | return; /* skip empty commands */ |
2879 | | | 2879 | |
2880 | if (targets == NULL) { | | 2880 | if (targets == NULL) { |
2881 | Parse_Error(PARSE_FATAL, "Unassociated shell command \"%s\"", p); | | 2881 | Parse_Error(PARSE_FATAL, "Unassociated shell command \"%s\"", p); |
2882 | return; | | 2882 | return; |
2883 | } | | 2883 | } |
2884 | | | 2884 | |
2885 | { | | 2885 | { |
2886 | char *cmd = bmake_strdup(p); | | 2886 | char *cmd = bmake_strdup(p); |
2887 | GNodeListNode *ln; | | 2887 | GNodeListNode *ln; |
2888 | | | 2888 | |
2889 | for (ln = targets->first; ln != NULL; ln = ln->next) { | | 2889 | for (ln = targets->first; ln != NULL; ln = ln->next) { |
2890 | GNode *gn = ln->datum; | | 2890 | GNode *gn = ln->datum; |
2891 | ParseAddCmd(gn, cmd); | | 2891 | ParseAddCmd(gn, cmd); |
2892 | } | | 2892 | } |
2893 | #ifdef CLEANUP | | 2893 | #ifdef CLEANUP |
2894 | Lst_Append(targCmds, cmd); | | 2894 | Lst_Append(targCmds, cmd); |
2895 | #endif | | 2895 | #endif |
2896 | } | | 2896 | } |
2897 | } | | 2897 | } |
2898 | | | 2898 | |
2899 | static Boolean | | 2899 | static Boolean |
2900 | ParseDirective(char *line) | | 2900 | ParseDirective(char *line) |
2901 | { | | 2901 | { |
2902 | char *cp; | | 2902 | char *cp; |
2903 | | | 2903 | |
2904 | if (*line == '.') { | | 2904 | if (*line == '.') { |
2905 | /* | | 2905 | /* |
2906 | * Lines that begin with the special character may be | | 2906 | * Lines that begin with the special character may be |
2907 | * include or undef directives. | | 2907 | * include or undef directives. |
2908 | * On the other hand they can be suffix rules (.c.o: ...) | | 2908 | * On the other hand they can be suffix rules (.c.o: ...) |
2909 | * or just dependencies for filenames that start '.'. | | 2909 | * or just dependencies for filenames that start '.'. |
2910 | */ | | 2910 | */ |
2911 | cp = line + 1; | | 2911 | cp = line + 1; |
2912 | pp_skip_whitespace(&cp); | | 2912 | pp_skip_whitespace(&cp); |
2913 | if (IsInclude(cp, FALSE)) { | | 2913 | if (IsInclude(cp, FALSE)) { |
2914 | ParseDoInclude(cp); | | 2914 | ParseDoInclude(cp); |
2915 | return TRUE; | | 2915 | return TRUE; |
2916 | } | | 2916 | } |
2917 | if (strncmp(cp, "undef", 5) == 0) { | | 2917 | if (strncmp(cp, "undef", 5) == 0) { |
2918 | const char *varname; | | 2918 | const char *varname; |
2919 | cp += 5; | | 2919 | cp += 5; |
2920 | pp_skip_whitespace(&cp); | | 2920 | pp_skip_whitespace(&cp); |
2921 | varname = cp; | | 2921 | varname = cp; |
2922 | for (; !ch_isspace(*cp) && *cp != '\0'; cp++) | | 2922 | for (; !ch_isspace(*cp) && *cp != '\0'; cp++) |
2923 | continue; | | 2923 | continue; |
2924 | *cp = '\0'; | | 2924 | *cp = '\0'; |
2925 | Var_Delete(varname, VAR_GLOBAL); | | 2925 | Var_Delete(varname, VAR_GLOBAL); |
2926 | /* TODO: undefine all variables, not only the first */ | | 2926 | /* TODO: undefine all variables, not only the first */ |
2927 | /* TODO: use Str_Words, like everywhere else */ | | 2927 | /* TODO: use Str_Words, like everywhere else */ |
2928 | return TRUE; | | 2928 | return TRUE; |
2929 | } else if (strncmp(cp, "export", 6) == 0) { | | 2929 | } else if (strncmp(cp, "export", 6) == 0) { |
2930 | cp += 6; | | 2930 | cp += 6; |
2931 | pp_skip_whitespace(&cp); | | 2931 | pp_skip_whitespace(&cp); |
2932 | Var_Export(cp, TRUE); | | 2932 | Var_Export(cp, TRUE); |
2933 | return TRUE; | | 2933 | return TRUE; |
2934 | } else if (strncmp(cp, "unexport", 8) == 0) { | | 2934 | } else if (strncmp(cp, "unexport", 8) == 0) { |
2935 | Var_UnExport(cp); | | 2935 | Var_UnExport(cp); |
2936 | return TRUE; | | 2936 | return TRUE; |
2937 | } else if (strncmp(cp, "info", 4) == 0 || | | 2937 | } else if (strncmp(cp, "info", 4) == 0 || |
2938 | strncmp(cp, "error", 5) == 0 || | | 2938 | strncmp(cp, "error", 5) == 0 || |
2939 | strncmp(cp, "warning", 7) == 0) { | | 2939 | strncmp(cp, "warning", 7) == 0) { |
2940 | if (ParseMessage(cp)) | | 2940 | if (ParseMessage(cp)) |
2941 | return TRUE; | | 2941 | return TRUE; |
2942 | } | | 2942 | } |
2943 | } | | 2943 | } |
2944 | return FALSE; | | 2944 | return FALSE; |
2945 | } | | 2945 | } |
2946 | | | 2946 | |
2947 | static Boolean | | 2947 | static Boolean |
2948 | ParseVarassign(const char *line) | | 2948 | ParseVarassign(const char *line) |
2949 | { | | 2949 | { |
2950 | VarAssign var; | | 2950 | VarAssign var; |
2951 | if (Parse_IsVar(line, &var)) { | | 2951 | if (Parse_IsVar(line, &var)) { |
2952 | FinishDependencyGroup(); | | 2952 | FinishDependencyGroup(); |
2953 | Parse_DoVar(&var, VAR_GLOBAL); | | 2953 | Parse_DoVar(&var, VAR_GLOBAL); |
2954 | return TRUE; | | 2954 | return TRUE; |
2955 | } | | 2955 | } |
2956 | return FALSE; | | 2956 | return FALSE; |
2957 | } | | 2957 | } |
2958 | | | 2958 | |
2959 | static char * | | 2959 | static char * |
2960 | FindSemicolon(char *p) | | 2960 | FindSemicolon(char *p) |
2961 | { | | 2961 | { |
2962 | int level = 0; | | 2962 | int level = 0; |
2963 | | | 2963 | |
2964 | for (; *p != '\0'; p++) { | | 2964 | for (; *p != '\0'; p++) { |
2965 | if (*p == '\\' && p[1] != '\0') { | | 2965 | if (*p == '\\' && p[1] != '\0') { |
2966 | p++; | | 2966 | p++; |
2967 | continue; | | 2967 | continue; |
2968 | } | | 2968 | } |
2969 | | | 2969 | |
2970 | if (*p == '$' && (p[1] == '(' || p[1] == '{')) { | | 2970 | if (*p == '$' && (p[1] == '(' || p[1] == '{')) { |
2971 | level++; | | 2971 | level++; |
2972 | continue; | | 2972 | continue; |
2973 | } | | 2973 | } |
2974 | | | 2974 | |
2975 | if (level > 0 && (*p == ')' || *p == '}')) { | | 2975 | if (level > 0 && (*p == ')' || *p == '}')) { |
2976 | level--; | | 2976 | level--; |
2977 | continue; | | 2977 | continue; |
2978 | } | | 2978 | } |
2979 | | | 2979 | |
2980 | if (level == 0 && *p == ';') { | | 2980 | if (level == 0 && *p == ';') { |
2981 | break; | | 2981 | break; |
2982 | } | | 2982 | } |
2983 | } | | 2983 | } |
2984 | return p; | | 2984 | return p; |
2985 | } | | 2985 | } |
2986 | | | 2986 | |
2987 | /* dependency -> target... op [source...] | | 2987 | /* dependency -> target... op [source...] |
2988 | * op -> ':' | '::' | '!' */ | | 2988 | * op -> ':' | '::' | '!' */ |
2989 | static void | | 2989 | static void |
2990 | ParseDependency(char *line) | | 2990 | ParseDependency(char *line) |
2991 | { | | 2991 | { |
2992 | VarEvalFlags eflags; | | 2992 | VarEvalFlags eflags; |
2993 | char *expanded_line; | | 2993 | char *expanded_line; |
2994 | const char *shellcmd = NULL; | | 2994 | const char *shellcmd = NULL; |
2995 | | | 2995 | |
2996 | /* | | 2996 | /* |
2997 | * For some reason - probably to make the parser impossible - | | 2997 | * For some reason - probably to make the parser impossible - |
2998 | * a ';' can be used to separate commands from dependencies. | | 2998 | * a ';' can be used to separate commands from dependencies. |
2999 | * Attempt to avoid ';' inside substitution patterns. | | 2999 | * Attempt to avoid ';' inside substitution patterns. |
3000 | */ | | 3000 | */ |
3001 | { | | 3001 | { |
3002 | char *semicolon = FindSemicolon(line); | | 3002 | char *semicolon = FindSemicolon(line); |
3003 | if (*semicolon != '\0') { | | 3003 | if (*semicolon != '\0') { |
3004 | /* Terminate the dependency list at the ';' */ | | 3004 | /* Terminate the dependency list at the ';' */ |
3005 | *semicolon = '\0'; | | 3005 | *semicolon = '\0'; |
3006 | shellcmd = semicolon + 1; | | 3006 | shellcmd = semicolon + 1; |
3007 | } | | 3007 | } |
3008 | } | | 3008 | } |
3009 | | | 3009 | |
3010 | /* | | 3010 | /* |
3011 | * We now know it's a dependency line so it needs to have all | | 3011 | * We now know it's a dependency line so it needs to have all |
3012 | * variables expanded before being parsed. | | 3012 | * variables expanded before being parsed. |
3013 | * | | 3013 | * |
3014 | * XXX: Ideally the dependency line would first be split into | | 3014 | * XXX: Ideally the dependency line would first be split into |
3015 | * its left-hand side, dependency operator and right-hand side, | | 3015 | * its left-hand side, dependency operator and right-hand side, |
3016 | * and then each side would be expanded on its own. This would | | 3016 | * and then each side would be expanded on its own. This would |
3017 | * allow for the left-hand side to allow only defined variables | | 3017 | * allow for the left-hand side to allow only defined variables |
3018 | * and to allow variables on the right-hand side to be undefined | | 3018 | * and to allow variables on the right-hand side to be undefined |
3019 | * as well. | | 3019 | * as well. |
3020 | * | | 3020 | * |
3021 | * Parsing the line first would also prevent that targets | | 3021 | * Parsing the line first would also prevent that targets |
3022 | * generated from variable expressions are interpreted as the | | 3022 | * generated from variable expressions are interpreted as the |
3023 | * dependency operator, such as in "target${:U:} middle: source", | | 3023 | * dependency operator, such as in "target${:U:} middle: source", |
3024 | * in which the middle is interpreted as a source, not a target. | | 3024 | * in which the middle is interpreted as a source, not a target. |
3025 | */ | | 3025 | */ |
3026 | | | 3026 | |
3027 | /* In lint mode, allow undefined variables to appear in | | 3027 | /* In lint mode, allow undefined variables to appear in |
3028 | * dependency lines. | | 3028 | * dependency lines. |
3029 | * | | 3029 | * |
3030 | * Ideally, only the right-hand side would allow undefined | | 3030 | * Ideally, only the right-hand side would allow undefined |
3031 | * variables since it is common to have no dependencies. | | 3031 | * variables since it is common to have no dependencies. |
3032 | * Having undefined variables on the left-hand side is more | | 3032 | * Having undefined variables on the left-hand side is more |
3033 | * unusual though. Since both sides are expanded in a single | | 3033 | * unusual though. Since both sides are expanded in a single |
3034 | * pass, there is not much choice what to do here. | | 3034 | * pass, there is not much choice what to do here. |
3035 | * | | 3035 | * |
3036 | * In normal mode, it does not matter whether undefined | | 3036 | * In normal mode, it does not matter whether undefined |
3037 | * variables are allowed or not since as of 2020-09-14, | | 3037 | * variables are allowed or not since as of 2020-09-14, |
3038 | * Var_Parse does not print any parse errors in such a case. | | 3038 | * Var_Parse does not print any parse errors in such a case. |
3039 | * It simply returns the special empty string var_Error, | | 3039 | * It simply returns the special empty string var_Error, |
3040 | * which cannot be detected in the result of Var_Subst. */ | | 3040 | * which cannot be detected in the result of Var_Subst. */ |
3041 | eflags = DEBUG(LINT) ? VARE_WANTRES : VARE_UNDEFERR | VARE_WANTRES; | | 3041 | eflags = DEBUG(LINT) ? VARE_WANTRES : VARE_UNDEFERR | VARE_WANTRES; |
3042 | (void)Var_Subst(line, VAR_CMDLINE, eflags, &expanded_line); | | 3042 | (void)Var_Subst(line, VAR_CMDLINE, eflags, &expanded_line); |
3043 | /* TODO: handle errors */ | | 3043 | /* TODO: handle errors */ |
3044 | | | 3044 | |
3045 | /* Need a fresh list for the target nodes */ | | 3045 | /* Need a fresh list for the target nodes */ |
3046 | if (targets != NULL) | | 3046 | if (targets != NULL) |
3047 | Lst_Free(targets); | | 3047 | Lst_Free(targets); |
3048 | targets = Lst_New(); | | 3048 | targets = Lst_New(); |
3049 | | | 3049 | |
3050 | ParseDoDependency(expanded_line); | | 3050 | ParseDoDependency(expanded_line); |
3051 | free(expanded_line); | | 3051 | free(expanded_line); |
3052 | | | 3052 | |
3053 | if (shellcmd != NULL) | | 3053 | if (shellcmd != NULL) |
3054 | ParseLine_ShellCommand(shellcmd); | | 3054 | ParseLine_ShellCommand(shellcmd); |
3055 | } | | 3055 | } |
3056 | | | 3056 | |
3057 | static void | | 3057 | static void |
3058 | ParseLine(char *line) | | 3058 | ParseLine(char *line) |
3059 | { | | 3059 | { |
3060 | if (ParseDirective(line)) | | 3060 | if (ParseDirective(line)) |
3061 | return; | | 3061 | return; |
3062 | | | 3062 | |
3063 | if (*line == '\t') { | | 3063 | if (*line == '\t') { |
3064 | ParseLine_ShellCommand(line + 1); | | 3064 | ParseLine_ShellCommand(line + 1); |
3065 | return; | | 3065 | return; |
3066 | } | | 3066 | } |
3067 | | | 3067 | |
3068 | #ifdef SYSVINCLUDE | | 3068 | #ifdef SYSVINCLUDE |
3069 | if (IsSysVInclude(line)) { | | 3069 | if (IsSysVInclude(line)) { |
3070 | /* | | 3070 | /* |
3071 | * It's an S3/S5-style "include". | | 3071 | * It's an S3/S5-style "include". |
3072 | */ | | 3072 | */ |
3073 | ParseTraditionalInclude(line); | | 3073 | ParseTraditionalInclude(line); |
3074 | return; | | 3074 | return; |
3075 | } | | 3075 | } |
3076 | #endif | | 3076 | #endif |
3077 | | | 3077 | |
3078 | #ifdef GMAKEEXPORT | | 3078 | #ifdef GMAKEEXPORT |
3079 | if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) && | | 3079 | if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) && |
3080 | strchr(line, ':') == NULL) { | | 3080 | strchr(line, ':') == NULL) { |
3081 | /* | | 3081 | /* |
3082 | * It's a Gmake "export". | | 3082 | * It's a Gmake "export". |
3083 | */ | | 3083 | */ |
3084 | ParseGmakeExport(line); | | 3084 | ParseGmakeExport(line); |
3085 | return; | | 3085 | return; |
3086 | } | | 3086 | } |
3087 | #endif | | 3087 | #endif |
3088 | | | 3088 | |
3089 | if (ParseVarassign(line)) | | 3089 | if (ParseVarassign(line)) |
3090 | return; | | 3090 | return; |
3091 | | | 3091 | |
3092 | FinishDependencyGroup(); | | 3092 | FinishDependencyGroup(); |
3093 | | | 3093 | |
3094 | ParseDependency(line); | | 3094 | ParseDependency(line); |
3095 | } | | 3095 | } |
3096 | | | 3096 | |
3097 | /* Parse a top-level makefile into its component parts, incorporating them | | 3097 | /* Parse a top-level makefile into its component parts, incorporating them |
3098 | * into the global dependency graph. | | 3098 | * into the global dependency graph. |
3099 | * | | 3099 | * |
3100 | * Input: | | 3100 | * Input: |
3101 | * name The name of the file being read | | 3101 | * name The name of the file being read |
3102 | * fd The open file to parse; will be closed at the end | | 3102 | * fd The open file to parse; will be closed at the end |
3103 | */ | | 3103 | */ |
3104 | void | | 3104 | void |
3105 | Parse_File(const char *name, int fd) | | 3105 | Parse_File(const char *name, int fd) |
3106 | { | | 3106 | { |
3107 | char *line; /* the line we're working on */ | | 3107 | char *line; /* the line we're working on */ |
3108 | struct loadedfile *lf; | | 3108 | struct loadedfile *lf; |
3109 | | | 3109 | |
3110 | lf = loadfile(name, fd); | | 3110 | lf = loadfile(name, fd); |
3111 | | | 3111 | |
3112 | assert(targets == NULL); | | 3112 | assert(targets == NULL); |
3113 | fatals = 0; | | 3113 | fatals = 0; |
3114 | | | 3114 | |
3115 | if (name == NULL) | | 3115 | if (name == NULL) |
3116 | name = "(stdin)"; | | 3116 | name = "(stdin)"; |
3117 | | | 3117 | |
3118 | Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf); | | 3118 | Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf); |
3119 | CurFile()->lf = lf; | | 3119 | CurFile()->lf = lf; |
3120 | | | 3120 | |
3121 | do { | | 3121 | do { |
3122 | while ((line = ParseReadLine()) != NULL) { | | 3122 | while ((line = ParseReadLine()) != NULL) { |
3123 | DEBUG2(PARSE, "ParseReadLine (%d): '%s'\n", | | 3123 | DEBUG2(PARSE, "ParseReadLine (%d): '%s'\n", |
3124 | CurFile()->lineno, line); | | 3124 | CurFile()->lineno, line); |
3125 | ParseLine(line); | | 3125 | ParseLine(line); |
3126 | } | | 3126 | } |
3127 | /* | | 3127 | /* |
3128 | * Reached EOF, but it may be just EOF of an include file... | | 3128 | * Reached EOF, but it may be just EOF of an include file... |
3129 | */ | | 3129 | */ |
3130 | } while (ParseEOF()); | | 3130 | } while (ParseEOF()); |
3131 | | | 3131 | |
3132 | FinishDependencyGroup(); | | 3132 | FinishDependencyGroup(); |
3133 | | | 3133 | |
3134 | if (fatals) { | | 3134 | if (fatals) { |
3135 | (void)fflush(stdout); | | 3135 | (void)fflush(stdout); |
3136 | (void)fprintf(stderr, | | 3136 | (void)fprintf(stderr, |
3137 | "%s: Fatal errors encountered -- cannot continue", | | 3137 | "%s: Fatal errors encountered -- cannot continue", |
3138 | progname); | | 3138 | progname); |
3139 | PrintOnError(NULL, NULL); | | 3139 | PrintOnError(NULL, NULL); |
3140 | exit(1); | | 3140 | exit(1); |
3141 | } | | 3141 | } |
3142 | } | | 3142 | } |
3143 | | | 3143 | |
3144 | /* Initialize the parsing module. */ | | 3144 | /* Initialize the parsing module. */ |
3145 | void | | 3145 | void |
3146 | Parse_Init(void) | | 3146 | Parse_Init(void) |
3147 | { | | 3147 | { |
3148 | mainNode = NULL; | | 3148 | mainNode = NULL; |
3149 | parseIncPath = Lst_New(); | | 3149 | parseIncPath = Lst_New(); |
3150 | sysIncPath = Lst_New(); | | 3150 | sysIncPath = Lst_New(); |
3151 | defSysIncPath = Lst_New(); | | 3151 | defSysIncPath = Lst_New(); |
3152 | Vector_Init(&includes, sizeof(IFile)); | | 3152 | Vector_Init(&includes, sizeof(IFile)); |
3153 | #ifdef CLEANUP | | 3153 | #ifdef CLEANUP |
3154 | targCmds = Lst_New(); | | 3154 | targCmds = Lst_New(); |
3155 | #endif | | 3155 | #endif |
3156 | } | | 3156 | } |
3157 | | | 3157 | |
3158 | /* Clean up the parsing module. */ | | 3158 | /* Clean up the parsing module. */ |
3159 | void | | 3159 | void |
3160 | Parse_End(void) | | 3160 | Parse_End(void) |
3161 | { | | 3161 | { |
3162 | #ifdef CLEANUP | | 3162 | #ifdef CLEANUP |
3163 | Lst_Destroy(targCmds, free); | | 3163 | Lst_Destroy(targCmds, free); |
3164 | assert(targets == NULL); | | 3164 | assert(targets == NULL); |
3165 | Lst_Destroy(defSysIncPath, Dir_Destroy); | | 3165 | Lst_Destroy(defSysIncPath, Dir_Destroy); |
3166 | Lst_Destroy(sysIncPath, Dir_Destroy); | | 3166 | Lst_Destroy(sysIncPath, Dir_Destroy); |
3167 | Lst_Destroy(parseIncPath, Dir_Destroy); | | 3167 | Lst_Destroy(parseIncPath, Dir_Destroy); |
3168 | assert(includes.len == 0); | | 3168 | assert(includes.len == 0); |
3169 | Vector_Done(&includes); | | 3169 | Vector_Done(&includes); |
3170 | #endif | | 3170 | #endif |
3171 | } | | 3171 | } |
3172 | | | 3172 | |
3173 | | | 3173 | |
3174 | /*- | | 3174 | /*- |
3175 | *----------------------------------------------------------------------- | | 3175 | *----------------------------------------------------------------------- |
3176 | * Parse_MainName -- | | 3176 | * Parse_MainName -- |
3177 | * Return a Lst of the main target to create for main()'s sake. If | | 3177 | * Return a Lst of the main target to create for main()'s sake. If |
3178 | * no such target exists, we Punt with an obnoxious error message. | | 3178 | * no such target exists, we Punt with an obnoxious error message. |
3179 | * | | 3179 | * |
3180 | * Results: | | 3180 | * Results: |
3181 | * A Lst of the single node to create. | | 3181 | * A Lst of the single node to create. |
3182 | * | | 3182 | * |
3183 | * Side Effects: | | 3183 | * Side Effects: |
3184 | * None. | | 3184 | * None. |
3185 | * | | 3185 | * |
3186 | *----------------------------------------------------------------------- | | 3186 | *----------------------------------------------------------------------- |
3187 | */ | | 3187 | */ |
3188 | GNodeList * | | 3188 | GNodeList * |
3189 | Parse_MainName(void) | | 3189 | Parse_MainName(void) |
3190 | { | | 3190 | { |
3191 | GNodeList *mainList; | | 3191 | GNodeList *mainList; |
3192 | | | 3192 | |
3193 | mainList = Lst_New(); | | 3193 | mainList = Lst_New(); |
3194 | | | 3194 | |
3195 | if (mainNode == NULL) { | | 3195 | if (mainNode == NULL) { |
3196 | Punt("no target to make."); | | 3196 | Punt("no target to make."); |
3197 | /*NOTREACHED*/ | | 3197 | /*NOTREACHED*/ |
3198 | } else if (mainNode->type & OP_DOUBLEDEP) { | | 3198 | } else if (mainNode->type & OP_DOUBLEDEP) { |
3199 | Lst_Append(mainList, mainNode); | | 3199 | Lst_Append(mainList, mainNode); |
3200 | Lst_AppendAll(mainList, mainNode->cohorts); | | 3200 | Lst_AppendAll(mainList, mainNode->cohorts); |
3201 | } else | | 3201 | } else |
3202 | Lst_Append(mainList, mainNode); | | 3202 | Lst_Append(mainList, mainNode); |
3203 | Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL); | | 3203 | Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL); |
3204 | return mainList; | | 3204 | return mainList; |
3205 | } | | 3205 | } |