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