Merge from vendor branch GCC:
[dragonfly.git] / contrib / binutils-2.14 / gas / cond.c
1 /* cond.c - conditional assembly pseudo-ops, and .include
2    Copyright 1990, 1991, 1992, 1993, 1995, 1997, 1998, 2000, 2001
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
28    scanned.  */
29 struct obstack cond_obstack;
30
31 struct file_line {
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   /* The source file & line number of the "if".  */
41   struct file_line if_file_line;
42   /* The source file & line of the "else".  */
43   struct file_line else_file_line;
44   /* The previous conditional.  */
45   struct conditional_frame *previous_cframe;
46   /* Have we seen an else yet?  */
47   int else_seen;
48   /* Whether we are currently ignoring input.  */
49   int ignoring;
50   /* Whether a conditional at a higher level is ignoring input.
51      Set also when a branch of an "if .. elseif .." tree has matched
52      to prevent further matches.  */
53   int dead_tree;
54   /* Macro nesting level at which this conditional was created.  */
55   int macro_nest;
56 };
57
58 static void initialize_cframe PARAMS ((struct conditional_frame *cframe));
59 static char *get_mri_string PARAMS ((int, int *));
60
61 static struct conditional_frame *current_cframe = NULL;
62
63 /* Performs the .ifdef (test_defined == 1) and
64    the .ifndef (test_defined == 0) pseudo op.  */
65
66 void
67 s_ifdef (test_defined)
68      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)
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 (arg)
127      int arg;
128 {
129   expressionS operand;
130   struct conditional_frame cframe;
131   int t;
132   char *stop = NULL;
133   char stopc;
134
135   if (flag_mri)
136     stop = mri_comment_field (&stopc);
137
138   /* Leading whitespace is part of operand.  */
139   SKIP_WHITESPACE ();
140
141   if (current_cframe != NULL && current_cframe->ignoring)
142     {
143       operand.X_add_number = 0;
144       while (! is_end_of_line[(unsigned char) *input_line_pointer])
145         ++input_line_pointer;
146     }
147   else
148     {
149       expression (&operand);
150       if (operand.X_op != O_constant)
151         as_bad (_("non-constant expression in \".if\" statement"));
152     }
153
154   switch ((operatorT) arg)
155     {
156     case O_eq: t = operand.X_add_number == 0; break;
157     case O_ne: t = operand.X_add_number != 0; break;
158     case O_lt: t = operand.X_add_number < 0; break;
159     case O_le: t = operand.X_add_number <= 0; break;
160     case O_ge: t = operand.X_add_number >= 0; break;
161     case O_gt: t = operand.X_add_number > 0; break;
162     default:
163       abort ();
164       return;
165     }
166
167   /* If the above error is signaled, this will dispatch
168      using an undefined result.  No big deal.  */
169   initialize_cframe (&cframe);
170   cframe.ignoring = cframe.dead_tree || ! t;
171   current_cframe = ((struct conditional_frame *)
172                     obstack_copy (&cond_obstack, &cframe, sizeof (cframe)));
173
174   if (LISTING_SKIP_COND ()
175       && cframe.ignoring
176       && (cframe.previous_cframe == NULL
177           || ! cframe.previous_cframe->ignoring))
178     listing_list (2);
179
180   if (flag_mri)
181     mri_comment_end (stop, stopc);
182
183   demand_empty_rest_of_line ();
184 }
185
186 /* Get a string for the MRI IFC or IFNC pseudo-ops.  */
187
188 static char *
189 get_mri_string (terminator, len)
190      int terminator;
191      int *len;
192 {
193   char *ret;
194   char *s;
195
196   SKIP_WHITESPACE ();
197   s = ret = input_line_pointer;
198   if (*input_line_pointer == '\'')
199     {
200       ++s;
201       ++input_line_pointer;
202       while (! is_end_of_line[(unsigned char) *input_line_pointer])
203         {
204           *s++ = *input_line_pointer++;
205           if (s[-1] == '\'')
206             {
207               if (*input_line_pointer != '\'')
208                 break;
209               ++input_line_pointer;
210             }
211         }
212       SKIP_WHITESPACE ();
213     }
214   else
215     {
216       while (*input_line_pointer != terminator
217              && ! is_end_of_line[(unsigned char) *input_line_pointer])
218         ++input_line_pointer;
219       s = input_line_pointer;
220       while (s > ret && (s[-1] == ' ' || s[-1] == '\t'))
221         --s;
222     }
223
224   *len = s - ret;
225   return ret;
226 }
227
228 /* The MRI IFC and IFNC pseudo-ops.  */
229
230 void
231 s_ifc (arg)
232      int arg;
233 {
234   char *stop = NULL;
235   char stopc;
236   char *s1, *s2;
237   int len1, len2;
238   int res;
239   struct conditional_frame cframe;
240
241   if (flag_mri)
242     stop = mri_comment_field (&stopc);
243
244   s1 = get_mri_string (',', &len1);
245
246   if (*input_line_pointer != ',')
247     as_bad (_("bad format for ifc or ifnc"));
248   else
249     ++input_line_pointer;
250
251   s2 = get_mri_string (';', &len2);
252
253   res = len1 == len2 && strncmp (s1, s2, len1) == 0;
254
255   initialize_cframe (&cframe);
256   cframe.ignoring = cframe.dead_tree || ! (res ^ arg);
257   current_cframe = ((struct conditional_frame *)
258                     obstack_copy (&cond_obstack, &cframe, sizeof (cframe)));
259
260   if (LISTING_SKIP_COND ()
261       && cframe.ignoring
262       && (cframe.previous_cframe == NULL
263           || ! cframe.previous_cframe->ignoring))
264     listing_list (2);
265
266   if (flag_mri)
267     mri_comment_end (stop, stopc);
268
269   demand_empty_rest_of_line ();
270 }
271
272 void
273 s_elseif (arg)
274      int arg;
275 {
276   if (current_cframe == NULL)
277     {
278       as_bad (_("\".elseif\" without matching \".if\""));
279     }
280   else if (current_cframe->else_seen)
281     {
282       as_bad (_("\".elseif\" after \".else\""));
283       as_bad_where (current_cframe->else_file_line.file,
284                     current_cframe->else_file_line.line,
285                     _("here is the previous \"else\""));
286       as_bad_where (current_cframe->if_file_line.file,
287                     current_cframe->if_file_line.line,
288                     _("here is the previous \"if\""));
289     }
290   else
291     {
292       as_where (&current_cframe->else_file_line.file,
293                 &current_cframe->else_file_line.line);
294
295       current_cframe->dead_tree |= !current_cframe->ignoring;
296       current_cframe->ignoring = current_cframe->dead_tree;
297     }
298
299   if (current_cframe == NULL || current_cframe->ignoring)
300     {
301       while (! is_end_of_line[(unsigned char) *input_line_pointer])
302         ++input_line_pointer;
303
304       if (current_cframe == NULL)
305         return;
306     }
307   else
308     {
309       expressionS operand;
310       int t;
311
312       /* Leading whitespace is part of operand.  */
313       SKIP_WHITESPACE ();
314
315       expression (&operand);
316       if (operand.X_op != O_constant)
317         as_bad (_("non-constant expression in \".elseif\" statement"));
318
319       switch ((operatorT) arg)
320         {
321         case O_eq: t = operand.X_add_number == 0; break;
322         case O_ne: t = operand.X_add_number != 0; break;
323         case O_lt: t = operand.X_add_number < 0; break;
324         case O_le: t = operand.X_add_number <= 0; break;
325         case O_ge: t = operand.X_add_number >= 0; break;
326         case O_gt: t = operand.X_add_number > 0; break;
327         default:
328           abort ();
329           return;
330         }
331
332       current_cframe->ignoring = current_cframe->dead_tree || ! t;
333     }
334
335   if (LISTING_SKIP_COND ()
336       && (current_cframe->previous_cframe == NULL
337           || ! current_cframe->previous_cframe->ignoring))
338     {
339       if (! current_cframe->ignoring)
340         listing_list (1);
341       else
342         listing_list (2);
343     }
344
345   demand_empty_rest_of_line ();
346 }
347
348 void
349 s_endif (arg)
350      int arg ATTRIBUTE_UNUSED;
351 {
352   struct conditional_frame *hold;
353
354   if (current_cframe == NULL)
355     {
356       as_bad (_("\".endif\" without \".if\""));
357     }
358   else
359     {
360       if (LISTING_SKIP_COND ()
361           && current_cframe->ignoring
362           && (current_cframe->previous_cframe == NULL
363               || ! current_cframe->previous_cframe->ignoring))
364         listing_list (1);
365
366       hold = current_cframe;
367       current_cframe = current_cframe->previous_cframe;
368       obstack_free (&cond_obstack, hold);
369     }                           /* if one pop too many */
370
371   if (flag_mri)
372     {
373       while (! is_end_of_line[(unsigned char) *input_line_pointer])
374         ++input_line_pointer;
375     }
376
377   demand_empty_rest_of_line ();
378 }
379
380 void
381 s_else (arg)
382      int arg ATTRIBUTE_UNUSED;
383 {
384   if (current_cframe == NULL)
385     {
386       as_bad (_("\".else\" without matching \".if\""));
387     }
388   else if (current_cframe->else_seen)
389     {
390       as_bad (_("duplicate \"else\""));
391       as_bad_where (current_cframe->else_file_line.file,
392                     current_cframe->else_file_line.line,
393                     _("here is the previous \"else\""));
394       as_bad_where (current_cframe->if_file_line.file,
395                     current_cframe->if_file_line.line,
396                     _("here is the previous \"if\""));
397     }
398   else
399     {
400       as_where (&current_cframe->else_file_line.file,
401                 &current_cframe->else_file_line.line);
402
403       current_cframe->ignoring =
404         current_cframe->dead_tree | !current_cframe->ignoring;
405
406       if (LISTING_SKIP_COND ()
407           && (current_cframe->previous_cframe == NULL
408               || ! current_cframe->previous_cframe->ignoring))
409         {
410           if (! current_cframe->ignoring)
411             listing_list (1);
412           else
413             listing_list (2);
414         }
415
416       current_cframe->else_seen = 1;
417     }
418
419   if (flag_mri)
420     {
421       while (! is_end_of_line[(unsigned char) *input_line_pointer])
422         ++input_line_pointer;
423     }
424
425   demand_empty_rest_of_line ();
426 }
427
428 void
429 s_ifeqs (arg)
430      int arg;
431 {
432   char *s1, *s2;
433   int len1, len2;
434   int res;
435   struct conditional_frame cframe;
436
437   s1 = demand_copy_C_string (&len1);
438
439   SKIP_WHITESPACE ();
440   if (*input_line_pointer != ',')
441     {
442       as_bad (_(".ifeqs syntax error"));
443       ignore_rest_of_line ();
444       return;
445     }
446
447   ++input_line_pointer;
448
449   s2 = demand_copy_C_string (&len2);
450
451   res = len1 == len2 && strncmp (s1, s2, len1) == 0;
452
453   initialize_cframe (&cframe);
454   cframe.ignoring = cframe.dead_tree || ! (res ^ arg);
455   current_cframe = ((struct conditional_frame *)
456                     obstack_copy (&cond_obstack, &cframe, sizeof (cframe)));
457
458   if (LISTING_SKIP_COND ()
459       && cframe.ignoring
460       && (cframe.previous_cframe == NULL
461           || ! cframe.previous_cframe->ignoring))
462     listing_list (2);
463
464   demand_empty_rest_of_line ();
465 }
466
467 int
468 ignore_input ()
469 {
470   char *s;
471
472   s = input_line_pointer;
473
474   if (NO_PSEUDO_DOT || flag_m68k_mri)
475     {
476       if (s[-1] != '.')
477         --s;
478     }
479   else
480     {
481       if (s[-1] != '.')
482         return (current_cframe != NULL) && (current_cframe->ignoring);
483     }
484
485   /* We cannot ignore certain pseudo ops.  */
486   if (((s[0] == 'i'
487         || s[0] == 'I')
488        && (!strncasecmp (s, "if", 2)
489            || !strncasecmp (s, "ifdef", 5)
490            || !strncasecmp (s, "ifndef", 6)))
491       || ((s[0] == 'e'
492            || s[0] == 'E')
493           && (!strncasecmp (s, "else", 4)
494               || !strncasecmp (s, "endif", 5)
495               || !strncasecmp (s, "endc", 4))))
496     return 0;
497
498   return (current_cframe != NULL) && (current_cframe->ignoring);
499 }
500
501 static void
502 initialize_cframe (cframe)
503      struct conditional_frame *cframe;
504 {
505   memset (cframe, 0, sizeof (*cframe));
506   as_where (&cframe->if_file_line.file,
507             &cframe->if_file_line.line);
508   cframe->previous_cframe = current_cframe;
509   cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring;
510   cframe->macro_nest = macro_nest;
511 }
512
513 /* Give an error if a conditional is unterminated inside a macro or
514    the assembly as a whole.  If NEST is non negative, we are being
515    called because of the end of a macro expansion.  If NEST is
516    negative, we are being called at the of the input files.  */
517
518 void
519 cond_finish_check (nest)
520      int nest;
521 {
522   if (current_cframe != NULL && current_cframe->macro_nest >= nest)
523     {
524       if (nest >= 0)
525         as_bad (_("end of macro inside conditional"));
526       else
527         as_bad (_("end of file inside conditional"));
528       as_bad_where (current_cframe->if_file_line.file,
529                     current_cframe->if_file_line.line,
530                     _("here is the start of the unterminated conditional"));
531       if (current_cframe->else_seen)
532         as_bad_where (current_cframe->else_file_line.file,
533                       current_cframe->else_file_line.line,
534                       _("here is the \"else\" of the unterminated conditional"));
535     }
536 }
537
538 /* This function is called when we exit out of a macro.  We assume
539    that any conditionals which began within the macro are correctly
540    nested, and just pop them off the stack.  */
541
542 void
543 cond_exit_macro (nest)
544      int nest;
545 {
546   while (current_cframe != NULL && current_cframe->macro_nest >= nest)
547     {
548       struct conditional_frame *hold;
549
550       hold = current_cframe;
551       current_cframe = current_cframe->previous_cframe;
552       obstack_free (&cond_obstack, hold);
553     }
554 }