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