/src/binutils-gdb/gas/cond.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* cond.c - conditional assembly pseudo-ops, and .include |
2 | | Copyright (C) 1990-2025 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of GAS, the GNU Assembler. |
5 | | |
6 | | GAS is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | GAS is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with GAS; see the file COPYING. If not, write to the Free |
18 | | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
19 | | 02110-1301, USA. */ |
20 | | |
21 | | #include "as.h" |
22 | | #include "sb.h" |
23 | | #include "macro.h" |
24 | | |
25 | | #include "obstack.h" |
26 | | |
27 | | /* This is allocated to grow and shrink as .ifdef/.endif pairs are |
28 | | scanned. */ |
29 | | struct obstack cond_obstack; |
30 | | |
31 | | struct file_line |
32 | | { |
33 | | const char *file; |
34 | | unsigned int line; |
35 | | }; |
36 | | |
37 | | /* We push one of these structures for each .if, and pop it at the |
38 | | .endif. */ |
39 | | |
40 | | struct conditional_frame |
41 | | { |
42 | | /* The source file & line number of the "if". */ |
43 | | struct file_line if_file_line; |
44 | | /* The source file & line of the "else". */ |
45 | | struct file_line else_file_line; |
46 | | /* The previous conditional. */ |
47 | | struct conditional_frame *previous_cframe; |
48 | | /* Have we seen an else yet? */ |
49 | | int else_seen; |
50 | | /* Whether we are currently ignoring input. */ |
51 | | int ignoring; |
52 | | /* Whether a conditional at a higher level is ignoring input. |
53 | | Set also when a branch of an "if .. elseif .." tree has matched |
54 | | to prevent further matches. */ |
55 | | int dead_tree; |
56 | | /* Macro nesting level at which this conditional was created. */ |
57 | | int macro_nest; |
58 | | }; |
59 | | |
60 | | static void initialize_cframe (struct conditional_frame *cframe); |
61 | | static char *get_mri_string (int, int *); |
62 | | |
63 | | static struct conditional_frame *current_cframe = NULL; |
64 | | |
65 | | /* Performs the .ifdef (test_defined == 1) and |
66 | | the .ifndef (test_defined == 0) pseudo op. */ |
67 | | |
68 | | void |
69 | | s_ifdef (int test_defined) |
70 | 252 | { |
71 | | /* Points to name of symbol. */ |
72 | 252 | char *name; |
73 | | /* Points to symbol. */ |
74 | 252 | symbolS *symbolP; |
75 | 252 | struct conditional_frame cframe; |
76 | 252 | char c; |
77 | | |
78 | | /* Leading whitespace is part of operand. */ |
79 | 252 | SKIP_WHITESPACE (); |
80 | 252 | name = input_line_pointer; |
81 | | |
82 | 252 | if (!is_name_beginner (*name) && *name != '"') |
83 | 232 | { |
84 | 232 | as_bad (_("invalid identifier for \".ifdef\"")); |
85 | 232 | obstack_1grow (&cond_obstack, 0); |
86 | 232 | ignore_rest_of_line (); |
87 | 232 | return; |
88 | 232 | } |
89 | | |
90 | 20 | c = get_symbol_name (& name); |
91 | 20 | symbolP = symbol_find (name); |
92 | 20 | (void) restore_line_pointer (c); |
93 | | |
94 | 20 | initialize_cframe (&cframe); |
95 | | |
96 | 20 | if (cframe.dead_tree) |
97 | 5 | cframe.ignoring = 1; |
98 | 15 | else |
99 | 15 | { |
100 | 15 | int is_defined; |
101 | | |
102 | | /* Use the same definition of 'defined' as .equiv so that a symbol |
103 | | which has been referenced but not yet given a value/address is |
104 | | considered to be undefined. */ |
105 | 15 | is_defined = |
106 | 15 | symbolP != NULL |
107 | 15 | && (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP)) |
108 | 15 | && S_GET_SEGMENT (symbolP) != reg_section; |
109 | | |
110 | 15 | cframe.ignoring = ! (test_defined ^ is_defined); |
111 | 15 | } |
112 | | |
113 | 20 | current_cframe = |
114 | 20 | (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); |
115 | 20 | memcpy (current_cframe, &cframe, sizeof cframe); |
116 | | |
117 | 20 | if (LISTING_SKIP_COND () |
118 | 20 | && cframe.ignoring |
119 | 20 | && (cframe.previous_cframe == NULL |
120 | 0 | || ! cframe.previous_cframe->ignoring)) |
121 | 0 | listing_list (2); |
122 | | |
123 | 20 | demand_empty_rest_of_line (); |
124 | 20 | } |
125 | | |
126 | | void |
127 | | s_if (int arg) |
128 | 311 | { |
129 | 311 | expressionS operand; |
130 | 311 | struct conditional_frame cframe; |
131 | 311 | int t; |
132 | 311 | char *stop = NULL; |
133 | 311 | char stopc = 0; |
134 | | |
135 | 311 | if (flag_mri) |
136 | 239 | stop = mri_comment_field (&stopc); |
137 | | |
138 | | /* Leading whitespace is part of operand. */ |
139 | 311 | SKIP_WHITESPACE (); |
140 | | |
141 | 311 | if (current_cframe != NULL && current_cframe->ignoring) |
142 | 5 | { |
143 | 5 | operand.X_add_number = 0; |
144 | 13 | while (! is_end_of_stmt (*input_line_pointer)) |
145 | 8 | ++input_line_pointer; |
146 | 5 | } |
147 | 306 | else |
148 | 306 | { |
149 | 306 | expression_and_evaluate (&operand); |
150 | 306 | if (operand.X_op != O_constant) |
151 | 250 | as_bad (_("non-constant expression in \".if\" statement")); |
152 | 306 | } |
153 | | |
154 | 311 | switch ((operatorT) arg) |
155 | 311 | { |
156 | 10 | case O_eq: t = operand.X_add_number == 0; break; |
157 | 35 | case O_ne: t = operand.X_add_number != 0; break; |
158 | 0 | case O_lt: t = operand.X_add_number < 0; break; |
159 | 266 | case O_le: t = operand.X_add_number <= 0; break; |
160 | 0 | case O_ge: t = operand.X_add_number >= 0; break; |
161 | 0 | case O_gt: t = operand.X_add_number > 0; break; |
162 | 0 | default: |
163 | 0 | abort (); |
164 | 0 | return; |
165 | 311 | } |
166 | | |
167 | | /* If the above error is signaled, this will dispatch |
168 | | using an undefined result. No big deal. */ |
169 | 311 | initialize_cframe (&cframe); |
170 | 311 | cframe.ignoring = cframe.dead_tree || ! t; |
171 | 311 | current_cframe = |
172 | 311 | (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); |
173 | 311 | memcpy (current_cframe, & cframe, sizeof cframe); |
174 | | |
175 | 311 | if (LISTING_SKIP_COND () |
176 | 311 | && cframe.ignoring |
177 | 311 | && (cframe.previous_cframe == NULL |
178 | 0 | || ! cframe.previous_cframe->ignoring)) |
179 | 0 | listing_list (2); |
180 | | |
181 | 311 | if (flag_mri) |
182 | 239 | mri_comment_end (stop, stopc); |
183 | | |
184 | 311 | demand_empty_rest_of_line (); |
185 | 311 | } |
186 | | |
187 | | /* Performs the .ifb (test_blank == 1) and |
188 | | the .ifnb (test_blank == 0) pseudo op. */ |
189 | | |
190 | | void |
191 | | s_ifb (int test_blank) |
192 | 0 | { |
193 | 0 | struct conditional_frame cframe; |
194 | |
|
195 | 0 | initialize_cframe (&cframe); |
196 | |
|
197 | 0 | if (cframe.dead_tree) |
198 | 0 | cframe.ignoring = 1; |
199 | 0 | else |
200 | 0 | { |
201 | 0 | int is_eol; |
202 | |
|
203 | 0 | SKIP_WHITESPACE (); |
204 | 0 | is_eol = is_end_of_stmt (*input_line_pointer); |
205 | 0 | cframe.ignoring = (test_blank == !is_eol); |
206 | 0 | } |
207 | |
|
208 | 0 | current_cframe = |
209 | 0 | (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); |
210 | 0 | memcpy (current_cframe, &cframe, sizeof cframe); |
211 | |
|
212 | 0 | if (LISTING_SKIP_COND () |
213 | 0 | && cframe.ignoring |
214 | 0 | && (cframe.previous_cframe == NULL |
215 | 0 | || ! cframe.previous_cframe->ignoring)) |
216 | 0 | listing_list (2); |
217 | |
|
218 | 0 | ignore_rest_of_line (); |
219 | 0 | } |
220 | | |
221 | | /* Get a string for the MRI IFC or IFNC pseudo-ops. */ |
222 | | |
223 | | static char * |
224 | | get_mri_string (int terminator, int *len) |
225 | 0 | { |
226 | 0 | char *ret; |
227 | 0 | char *s; |
228 | |
|
229 | 0 | SKIP_WHITESPACE (); |
230 | 0 | s = ret = input_line_pointer; |
231 | 0 | if (*input_line_pointer == '\'') |
232 | 0 | { |
233 | 0 | ++s; |
234 | 0 | ++input_line_pointer; |
235 | 0 | while (! is_end_of_stmt (*input_line_pointer)) |
236 | 0 | { |
237 | 0 | *s++ = *input_line_pointer++; |
238 | 0 | if (s[-1] == '\'') |
239 | 0 | { |
240 | 0 | if (*input_line_pointer != '\'') |
241 | 0 | break; |
242 | 0 | ++input_line_pointer; |
243 | 0 | } |
244 | 0 | } |
245 | 0 | SKIP_WHITESPACE (); |
246 | 0 | } |
247 | 0 | else |
248 | 0 | { |
249 | 0 | while (*input_line_pointer != terminator |
250 | 0 | && ! is_end_of_stmt (*input_line_pointer)) |
251 | 0 | ++input_line_pointer; |
252 | 0 | s = input_line_pointer; |
253 | 0 | while (s > ret && is_whitespace (s[-1])) |
254 | 0 | --s; |
255 | 0 | } |
256 | |
|
257 | 0 | *len = s - ret; |
258 | 0 | return ret; |
259 | 0 | } |
260 | | |
261 | | /* The MRI IFC and IFNC pseudo-ops. */ |
262 | | |
263 | | void |
264 | | s_ifc (int arg) |
265 | 0 | { |
266 | 0 | char *stop = NULL; |
267 | 0 | char stopc = 0; |
268 | 0 | char *s1, *s2; |
269 | 0 | int len1, len2; |
270 | 0 | int res; |
271 | 0 | struct conditional_frame cframe; |
272 | |
|
273 | 0 | if (flag_mri) |
274 | 0 | stop = mri_comment_field (&stopc); |
275 | |
|
276 | 0 | s1 = get_mri_string (',', &len1); |
277 | |
|
278 | 0 | if (*input_line_pointer != ',') |
279 | 0 | as_bad (_("bad format for ifc or ifnc")); |
280 | 0 | else |
281 | 0 | ++input_line_pointer; |
282 | |
|
283 | 0 | s2 = get_mri_string (';', &len2); |
284 | |
|
285 | 0 | res = len1 == len2 && strncmp (s1, s2, len1) == 0; |
286 | |
|
287 | 0 | initialize_cframe (&cframe); |
288 | 0 | cframe.ignoring = cframe.dead_tree || ! (res ^ arg); |
289 | 0 | current_cframe = |
290 | 0 | (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); |
291 | 0 | memcpy (current_cframe, &cframe, sizeof cframe); |
292 | | |
293 | 0 | if (LISTING_SKIP_COND () |
294 | 0 | && cframe.ignoring |
295 | 0 | && (cframe.previous_cframe == NULL |
296 | 0 | || ! cframe.previous_cframe->ignoring)) |
297 | 0 | listing_list (2); |
298 | |
|
299 | 0 | if (flag_mri) |
300 | 0 | mri_comment_end (stop, stopc); |
301 | |
|
302 | 0 | demand_empty_rest_of_line (); |
303 | 0 | } |
304 | | |
305 | | void |
306 | | s_elseif (int arg) |
307 | 0 | { |
308 | 0 | if (current_cframe == NULL) |
309 | 0 | { |
310 | 0 | as_bad (_("\".elseif\" without matching \".if\"")); |
311 | 0 | } |
312 | 0 | else if (current_cframe->else_seen) |
313 | 0 | { |
314 | 0 | as_bad (_("\".elseif\" after \".else\"")); |
315 | 0 | as_bad_where (current_cframe->else_file_line.file, |
316 | 0 | current_cframe->else_file_line.line, |
317 | 0 | _("here is the previous \".else\"")); |
318 | 0 | as_bad_where (current_cframe->if_file_line.file, |
319 | 0 | current_cframe->if_file_line.line, |
320 | 0 | _("here is the previous \".if\"")); |
321 | 0 | } |
322 | 0 | else |
323 | 0 | { |
324 | 0 | current_cframe->else_file_line.file |
325 | 0 | = as_where (¤t_cframe->else_file_line.line); |
326 | |
|
327 | 0 | current_cframe->dead_tree |= !current_cframe->ignoring; |
328 | 0 | current_cframe->ignoring = current_cframe->dead_tree; |
329 | 0 | } |
330 | |
|
331 | 0 | if (current_cframe == NULL || current_cframe->ignoring) |
332 | 0 | { |
333 | 0 | while (! is_end_of_stmt (*input_line_pointer)) |
334 | 0 | ++input_line_pointer; |
335 | |
|
336 | 0 | if (current_cframe == NULL) |
337 | 0 | return; |
338 | 0 | } |
339 | 0 | else |
340 | 0 | { |
341 | 0 | expressionS operand; |
342 | 0 | int t; |
343 | | |
344 | | /* Leading whitespace is part of operand. */ |
345 | 0 | SKIP_WHITESPACE (); |
346 | |
|
347 | 0 | expression_and_evaluate (&operand); |
348 | 0 | if (operand.X_op != O_constant) |
349 | 0 | as_bad (_("non-constant expression in \".elseif\" statement")); |
350 | |
|
351 | 0 | switch ((operatorT) arg) |
352 | 0 | { |
353 | 0 | case O_eq: t = operand.X_add_number == 0; break; |
354 | 0 | case O_ne: t = operand.X_add_number != 0; break; |
355 | 0 | case O_lt: t = operand.X_add_number < 0; break; |
356 | 0 | case O_le: t = operand.X_add_number <= 0; break; |
357 | 0 | case O_ge: t = operand.X_add_number >= 0; break; |
358 | 0 | case O_gt: t = operand.X_add_number > 0; break; |
359 | 0 | default: |
360 | 0 | abort (); |
361 | 0 | return; |
362 | 0 | } |
363 | | |
364 | 0 | current_cframe->ignoring = current_cframe->dead_tree || ! t; |
365 | 0 | } |
366 | | |
367 | 0 | if (LISTING_SKIP_COND () |
368 | 0 | && (current_cframe->previous_cframe == NULL |
369 | 0 | || ! current_cframe->previous_cframe->ignoring)) |
370 | 0 | { |
371 | 0 | if (! current_cframe->ignoring) |
372 | 0 | listing_list (1); |
373 | 0 | else |
374 | 0 | listing_list (2); |
375 | 0 | } |
376 | |
|
377 | 0 | demand_empty_rest_of_line (); |
378 | 0 | } |
379 | | |
380 | | void |
381 | | s_endif (int arg ATTRIBUTE_UNUSED) |
382 | 0 | { |
383 | 0 | struct conditional_frame *hold; |
384 | |
|
385 | 0 | if (current_cframe == NULL) |
386 | 0 | { |
387 | 0 | as_bad (_("\".endif\" without \".if\"")); |
388 | 0 | } |
389 | 0 | else |
390 | 0 | { |
391 | 0 | if (LISTING_SKIP_COND () |
392 | 0 | && current_cframe->ignoring |
393 | 0 | && (current_cframe->previous_cframe == NULL |
394 | 0 | || ! current_cframe->previous_cframe->ignoring)) |
395 | 0 | listing_list (1); |
396 | |
|
397 | 0 | hold = current_cframe; |
398 | 0 | current_cframe = current_cframe->previous_cframe; |
399 | 0 | obstack_free (&cond_obstack, hold); |
400 | 0 | } /* if one pop too many */ |
401 | |
|
402 | 0 | if (flag_mri) |
403 | 0 | { |
404 | 0 | while (! is_end_of_stmt (*input_line_pointer)) |
405 | 0 | ++input_line_pointer; |
406 | 0 | } |
407 | |
|
408 | 0 | demand_empty_rest_of_line (); |
409 | 0 | } |
410 | | |
411 | | void |
412 | | s_else (int arg ATTRIBUTE_UNUSED) |
413 | 7 | { |
414 | 7 | if (current_cframe == NULL) |
415 | 0 | { |
416 | 0 | as_bad (_("\".else\" without matching \".if\"")); |
417 | 0 | } |
418 | 7 | else if (current_cframe->else_seen) |
419 | 6 | { |
420 | 6 | as_bad (_("duplicate \".else\"")); |
421 | 6 | as_bad_where (current_cframe->else_file_line.file, |
422 | 6 | current_cframe->else_file_line.line, |
423 | 6 | _("here is the previous \".else\"")); |
424 | 6 | as_bad_where (current_cframe->if_file_line.file, |
425 | 6 | current_cframe->if_file_line.line, |
426 | 6 | _("here is the previous \".if\"")); |
427 | 6 | } |
428 | 1 | else |
429 | 1 | { |
430 | 1 | current_cframe->else_file_line.file |
431 | 1 | = as_where (¤t_cframe->else_file_line.line); |
432 | | |
433 | 1 | current_cframe->ignoring = |
434 | 1 | current_cframe->dead_tree | !current_cframe->ignoring; |
435 | | |
436 | 1 | if (LISTING_SKIP_COND () |
437 | 1 | && (current_cframe->previous_cframe == NULL |
438 | 0 | || ! current_cframe->previous_cframe->ignoring)) |
439 | 0 | { |
440 | 0 | if (! current_cframe->ignoring) |
441 | 0 | listing_list (1); |
442 | 0 | else |
443 | 0 | listing_list (2); |
444 | 0 | } |
445 | | |
446 | 1 | current_cframe->else_seen = 1; |
447 | 1 | } |
448 | | |
449 | 7 | if (flag_mri) |
450 | 7 | { |
451 | 7 | while (! is_end_of_stmt (*input_line_pointer)) |
452 | 0 | ++input_line_pointer; |
453 | 7 | } |
454 | | |
455 | 7 | demand_empty_rest_of_line (); |
456 | 7 | } |
457 | | |
458 | | void |
459 | | s_ifeqs (int arg) |
460 | 4 | { |
461 | 4 | char *s1, *s2; |
462 | 4 | int len1, len2; |
463 | 4 | int res; |
464 | 4 | struct conditional_frame cframe; |
465 | | |
466 | 4 | s1 = demand_copy_C_string (&len1); |
467 | | |
468 | 4 | SKIP_WHITESPACE (); |
469 | 4 | if (*input_line_pointer != ',') |
470 | 4 | { |
471 | 4 | as_bad (_(".ifeqs syntax error")); |
472 | 4 | ignore_rest_of_line (); |
473 | 4 | return; |
474 | 4 | } |
475 | | |
476 | 0 | ++input_line_pointer; |
477 | |
|
478 | 0 | s2 = demand_copy_C_string (&len2); |
479 | |
|
480 | 0 | res = len1 == len2 && strncmp (s1, s2, len1) == 0; |
481 | |
|
482 | 0 | initialize_cframe (&cframe); |
483 | 0 | cframe.ignoring = cframe.dead_tree || ! (res ^ arg); |
484 | 0 | current_cframe = |
485 | 0 | (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); |
486 | 0 | memcpy (current_cframe, &cframe, sizeof cframe); |
487 | |
|
488 | 0 | if (LISTING_SKIP_COND () |
489 | 0 | && cframe.ignoring |
490 | 0 | && (cframe.previous_cframe == NULL |
491 | 0 | || ! cframe.previous_cframe->ignoring)) |
492 | 0 | listing_list (2); |
493 | |
|
494 | 0 | demand_empty_rest_of_line (); |
495 | 0 | } |
496 | | |
497 | | int |
498 | | ignore_input (void) |
499 | 1.00M | { |
500 | 1.00M | char *s; |
501 | | |
502 | 1.00M | s = input_line_pointer; |
503 | | |
504 | 1.00M | if (NO_PSEUDO_DOT || flag_m68k_mri) |
505 | 0 | { |
506 | 0 | if (s[-1] != '.') |
507 | 0 | --s; |
508 | 0 | } |
509 | 1.00M | else |
510 | 1.00M | { |
511 | 1.00M | if (s[-1] != '.') |
512 | 858k | return (current_cframe != NULL) && (current_cframe->ignoring); |
513 | 1.00M | } |
514 | | |
515 | | /* We cannot ignore certain pseudo ops. */ |
516 | 150k | switch (s[0]) |
517 | 150k | { |
518 | 3.53k | case 'i': case 'I': |
519 | 3.53k | if (s[1] == 'f' || s[1] == 'F') |
520 | 1.78k | return 0; |
521 | 1.75k | break; |
522 | 14.4k | case 'e': case 'E': |
523 | 14.4k | if (!strncasecmp (s, "else", 4) |
524 | 14.4k | || !strncasecmp (s, "endif", 5) |
525 | 14.4k | || !strncasecmp (s, "endc", 4)) |
526 | 436 | return 0; |
527 | 13.9k | break; |
528 | 65.6k | case 'l': case 'L': |
529 | 65.6k | if (!strncasecmp (s, "linefile", 8)) |
530 | 42.1k | return 0; |
531 | 23.5k | break; |
532 | 150k | } |
533 | | |
534 | 106k | return (current_cframe != NULL) && (current_cframe->ignoring); |
535 | 150k | } |
536 | | |
537 | | static void |
538 | | initialize_cframe (struct conditional_frame *cframe) |
539 | 331 | { |
540 | 331 | memset (cframe, 0, sizeof (*cframe)); |
541 | 331 | cframe->if_file_line.file |
542 | 331 | = as_where (&cframe->if_file_line.line); |
543 | 331 | cframe->previous_cframe = current_cframe; |
544 | 331 | cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring; |
545 | 331 | cframe->macro_nest = macro_nest; |
546 | 331 | } |
547 | | |
548 | | /* Give an error if a conditional is unterminated inside a macro or |
549 | | the assembly as a whole. If NEST is non negative, we are being |
550 | | called because of the end of a macro expansion. If NEST is |
551 | | negative, we are being called at the of the input files. */ |
552 | | |
553 | | void |
554 | | cond_finish_check (int nest) |
555 | 1.21k | { |
556 | 1.21k | if (current_cframe != NULL && current_cframe->macro_nest >= nest) |
557 | 209 | { |
558 | 209 | if (nest >= 0) |
559 | 199 | as_bad (_("end of macro inside conditional")); |
560 | 10 | else |
561 | 10 | as_bad (_("end of file inside conditional")); |
562 | | |
563 | 209 | as_bad_where (current_cframe->if_file_line.file, |
564 | 209 | current_cframe->if_file_line.line, |
565 | 209 | _("here is the start of the unterminated conditional")); |
566 | 209 | if (current_cframe->else_seen) |
567 | 1 | as_bad_where (current_cframe->else_file_line.file, |
568 | 1 | current_cframe->else_file_line.line, |
569 | 1 | _("here is the \"else\" of the unterminated conditional")); |
570 | 209 | cond_exit_macro (nest); |
571 | 209 | } |
572 | 1.21k | } |
573 | | |
574 | | /* This function is called when we exit out of a macro. We assume |
575 | | that any conditionals which began within the macro are correctly |
576 | | nested, and just pop them off the stack. */ |
577 | | |
578 | | void |
579 | | cond_exit_macro (int nest) |
580 | 209 | { |
581 | 540 | while (current_cframe != NULL && current_cframe->macro_nest >= nest) |
582 | 331 | { |
583 | 331 | struct conditional_frame *hold; |
584 | | |
585 | 331 | hold = current_cframe; |
586 | 331 | current_cframe = current_cframe->previous_cframe; |
587 | 331 | obstack_free (&cond_obstack, hold); |
588 | 331 | } |
589 | 209 | } |