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