cond.c revision 89857
190075Sobrien/* cond.c - conditional assembly pseudo-ops, and .include
2169689Skan   Copyright 1990, 1991, 1992, 1993, 1995, 1997, 1998, 2000, 2001
390075Sobrien   Free Software Foundation, Inc.
4132718Skan
590075Sobrien   This file is part of GAS, the GNU Assembler.
6132718Skan
7132718Skan   GAS is free software; you can redistribute it and/or modify
8132718Skan   it under the terms of the GNU General Public License as published by
9132718Skan   the Free Software Foundation; either version 2, or (at your option)
1090075Sobrien   any later version.
11132718Skan
12132718Skan   GAS is distributed in the hope that it will be useful,
13132718Skan   but WITHOUT ANY WARRANTY; without even the implied warranty of
14132718Skan   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1590075Sobrien   GNU General Public License for more details.
16132718Skan
17132718Skan   You should have received a copy of the GNU General Public License
18169689Skan   along with GAS; see the file COPYING.  If not, write to the Free
19169689Skan   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
2090075Sobrien   02111-1307, USA.  */
2190075Sobrien
2296263Sobrien#include "as.h"
2390075Sobrien#include "macro.h"
2490075Sobrien
2590075Sobrien#include "obstack.h"
2690075Sobrien
27117395Skan/* This is allocated to grow and shrink as .ifdef/.endif pairs are
28117395Skan   scanned.  */
29117395Skanstruct obstack cond_obstack;
30117395Skan
31117395Skanstruct file_line {
32169689Skan  char *file;
33169689Skan  unsigned int line;
34169689Skan};
35169689Skan
36169689Skan/* We push one of these structures for each .if, and pop it at the
37169689Skan   .endif.  */
38169689Skan
39169689Skanstruct conditional_frame {
40169689Skan  /* The source file & line number of the "if".  */
41169689Skan  struct file_line if_file_line;
42169689Skan  /* The source file & line of the "else".  */
43169689Skan  struct file_line else_file_line;
44169689Skan  /* The previous conditional.  */
45169689Skan  struct conditional_frame *previous_cframe;
46169689Skan  /* 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\""));
261    }
262  else if (current_cframe->else_seen)
263    {
264      as_bad (_("\".elseif\" after \".else\""));
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\""));
369    }
370  else if (current_cframe->else_seen)
371    {
372      as_bad (_("duplicate \"else\""));
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