1/* cond.c - conditional assembly pseudo-ops, and .include
2   Copyright 1990, 1991, 1992, 1993, 1995, 1997, 1998, 2000, 2001, 2002,
3   2003, 2006 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, 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.  */
30struct obstack cond_obstack;
31
32struct 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
40struct 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
59static void initialize_cframe (struct conditional_frame *cframe);
60static char *get_mri_string (int, int *);
61
62static struct conditional_frame *current_cframe = NULL;
63
64/* Performs the .ifdef (test_defined == 1) and
65   the .ifndef (test_defined == 0) pseudo op.  */
66
67void
68s_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
125void
126s_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
188void
189s_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
221static char *
222get_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
261void
262s_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
302void
303s_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
377void
378s_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
408void
409s_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
455void
456s_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
493int
494ignore_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
527static void
528initialize_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
543void
544cond_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
566void
567cond_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