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