cond.c revision 38889
1260884Simp/* cond.c - conditional assembly pseudo-ops, and .include
2260884Simp   Copyright (C) 1990, 91, 92, 93, 95, 96, 97, 1998
3260884Simp   Free Software Foundation, Inc.
4260884Simp
5260884Simp   This file is part of GAS, the GNU Assembler.
6260884Simp
7260884Simp   GAS is free software; you can redistribute it and/or modify
8260884Simp   it under the terms of the GNU General Public License as published by
9260884Simp   the Free Software Foundation; either version 2, or (at your option)
10260884Simp   any later version.
11260884Simp
12260884Simp   GAS is distributed in the hope that it will be useful,
13260884Simp   but WITHOUT ANY WARRANTY; without even the implied warranty of
14260884Simp   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15260884Simp   GNU General Public License for more details.
16260884Simp
17260884Simp   You should have received a copy of the GNU General Public License
18260884Simp   along with GAS; see the file COPYING.  If not, write to the Free
19260884Simp   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20260884Simp   02111-1307, USA.  */
21260884Simp
22260884Simp#include "as.h"
23260884Simp#include "macro.h"
24260884Simp
25260884Simp#include "obstack.h"
26260884Simp
27260884Simp/* This is allocated to grow and shrink as .ifdef/.endif pairs are scanned. */
28260884Simpstruct obstack cond_obstack;
29260884Simp
30260884Simpstruct file_line
31260884Simp{
32260884Simp  char *file;
33260884Simp  unsigned int line;
34260884Simp};
35260884Simp
36260884Simp/* We push one of these structures for each .if, and pop it at the
37260884Simp   .endif.  */
38260884Simp
39260884Simpstruct conditional_frame
40260884Simp{
41260884Simp  /* The source file & line number of the "if".  */
42260884Simp  struct file_line if_file_line;
43260884Simp  /* The source file & line of the "else".  */
44260884Simp  struct file_line else_file_line;
45260884Simp  /* The previous conditional.  */
46260884Simp  struct conditional_frame *previous_cframe;
47260884Simp  /* Have we seen an else yet?  */
48260884Simp  int else_seen;
49260884Simp  /* Whether we are currently ignoring input.  */
50260884Simp  int ignoring;
51260884Simp  /* Whether a conditional at a higher level is ignoring input.  */
52260884Simp  int dead_tree;
53260884Simp  /* Macro nesting level at which this conditional was created.  */
54260884Simp  int macro_nest;
55260884Simp};
56260884Simp
57260884Simpstatic void initialize_cframe PARAMS ((struct conditional_frame *cframe));
58260884Simpstatic char *get_mri_string PARAMS ((int, int *));
59260884Simp
60260884Simpstatic struct conditional_frame *current_cframe = NULL;
61260884Simp
62260884Simpvoid
63260884Simps_ifdef (arg)
64260884Simp     int arg;
65260884Simp{
66260884Simp  register char *name;		/* points to name of symbol */
67260884Simp  register struct symbol *symbolP;	/* Points to symbol */
68260884Simp  struct conditional_frame cframe;
69260884Simp
70260884Simp  SKIP_WHITESPACE ();		/* Leading whitespace is part of operand. */
71260884Simp  name = input_line_pointer;
72260884Simp
73260884Simp  if (!is_name_beginner (*name))
74260884Simp    {
75260884Simp      as_bad ("invalid identifier for \".ifdef\"");
76260884Simp      obstack_1grow (&cond_obstack, 0);
77260884Simp      ignore_rest_of_line ();
78260884Simp    }
79260884Simp  else
80260884Simp    {
81260884Simp      char c;
82260884Simp
83260884Simp      c = get_symbol_end ();
84260884Simp      symbolP = symbol_find (name);
85260884Simp      *input_line_pointer = c;
86260884Simp
87260884Simp      initialize_cframe (&cframe);
88260884Simp      cframe.ignoring = cframe.dead_tree || !((symbolP != 0) ^ arg);
89260884Simp      current_cframe = ((struct conditional_frame *)
90260884Simp			obstack_copy (&cond_obstack, &cframe,
91260884Simp				      sizeof (cframe)));
92260884Simp
93260884Simp      if (LISTING_SKIP_COND ()
94260884Simp	  && cframe.ignoring
95260884Simp	  && (cframe.previous_cframe == NULL
96260884Simp	      || ! cframe.previous_cframe->ignoring))
97260884Simp	listing_list (2);
98260884Simp
99260884Simp      demand_empty_rest_of_line ();
100260884Simp    }				/* if a valid identifyer name */
101260884Simp}				/* s_ifdef() */
102260884Simp
103260884Simpvoid
104260884Simps_if (arg)
105260884Simp     int arg;
106260884Simp{
107260884Simp  expressionS operand;
108260884Simp  struct conditional_frame cframe;
109260884Simp  int t;
110260884Simp  char *stop = NULL;
111260884Simp  char stopc;
112260884Simp
113260884Simp  if (flag_mri)
114260884Simp    stop = mri_comment_field (&stopc);
115260884Simp
116260884Simp  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_endif (arg)
251     int arg;
252{
253  struct conditional_frame *hold;
254
255  if (current_cframe == NULL)
256    {
257      as_bad ("\".endif\" without \".if\"");
258    }
259  else
260    {
261      if (LISTING_SKIP_COND ()
262	  && current_cframe->ignoring
263	  && (current_cframe->previous_cframe == NULL
264	      || ! current_cframe->previous_cframe->ignoring))
265	listing_list (1);
266
267      hold = current_cframe;
268      current_cframe = current_cframe->previous_cframe;
269      obstack_free (&cond_obstack, hold);
270    }				/* if one pop too many */
271
272  if (flag_mri)
273    {
274      while (! is_end_of_line[(unsigned char) *input_line_pointer])
275	++input_line_pointer;
276    }
277
278  demand_empty_rest_of_line ();
279}				/* s_endif() */
280
281void
282s_else (arg)
283     int arg;
284{
285  if (current_cframe == NULL)
286    {
287      as_bad (".else without matching .if - ignored");
288
289    }
290  else if (current_cframe->else_seen)
291    {
292      as_bad ("duplicate \"else\" - ignored");
293      as_bad_where (current_cframe->else_file_line.file,
294		    current_cframe->else_file_line.line,
295		    "here is the previous \"else\"");
296      as_bad_where (current_cframe->if_file_line.file,
297		    current_cframe->if_file_line.line,
298		    "here is the previous \"if\"");
299    }
300  else
301    {
302      as_where (&current_cframe->else_file_line.file,
303		&current_cframe->else_file_line.line);
304
305      if (!current_cframe->dead_tree)
306	{
307	  current_cframe->ignoring = !current_cframe->ignoring;
308	  if (LISTING_SKIP_COND ())
309	    {
310	      if (! current_cframe->ignoring)
311		listing_list (1);
312	      else
313		listing_list (2);
314	    }
315	}			/* if not a dead tree */
316
317      current_cframe->else_seen = 1;
318    }				/* if error else do it */
319
320  if (flag_mri)
321    {
322      while (! is_end_of_line[(unsigned char) *input_line_pointer])
323	++input_line_pointer;
324    }
325
326  demand_empty_rest_of_line ();
327}				/* s_else() */
328
329void
330s_ifeqs (arg)
331     int arg;
332{
333  char *s1, *s2;
334  int len1, len2;
335  int res;
336  struct conditional_frame cframe;
337
338  s1 = demand_copy_C_string (&len1);
339
340  SKIP_WHITESPACE ();
341  if (*input_line_pointer != ',')
342    {
343      as_bad (".ifeqs syntax error");
344      ignore_rest_of_line ();
345      return;
346    }
347
348  ++input_line_pointer;
349
350  s2 = demand_copy_C_string (&len2);
351
352  res = len1 == len2 && strncmp (s1, s2, len1) == 0;
353
354  initialize_cframe (&cframe);
355  cframe.ignoring = cframe.dead_tree || ! (res ^ arg);
356  current_cframe = ((struct conditional_frame *)
357		    obstack_copy (&cond_obstack, &cframe, sizeof (cframe)));
358
359  if (LISTING_SKIP_COND ()
360      && cframe.ignoring
361      && (cframe.previous_cframe == NULL
362	  || ! cframe.previous_cframe->ignoring))
363    listing_list (2);
364
365  demand_empty_rest_of_line ();
366}				/* s_ifeqs() */
367
368int
369ignore_input ()
370{
371  char *s;
372
373  s = input_line_pointer;
374
375  if (flag_m68k_mri
376#ifdef NO_PSEUDO_DOT
377      || 1
378#endif
379      )
380    {
381      if (s[-1] != '.')
382	--s;
383    }
384  else
385    {
386      if (s[-1] != '.')
387	return (current_cframe != NULL) && (current_cframe->ignoring);
388    }
389
390  /* We cannot ignore certain pseudo ops.  */
391  if (((s[0] == 'i'
392	|| s[0] == 'I')
393       && (!strncasecmp (s, "if", 2)
394	   || !strncasecmp (s, "ifdef", 5)
395	   || !strncasecmp (s, "ifndef", 6)))
396      || ((s[0] == 'e'
397	   || s[0] == 'E')
398	  && (!strncasecmp (s, "else", 4)
399	      || !strncasecmp (s, "endif", 5)
400	      || !strncasecmp (s, "endc", 4))))
401    return 0;
402
403  return (current_cframe != NULL) && (current_cframe->ignoring);
404}				/* ignore_input() */
405
406static void
407initialize_cframe (cframe)
408     struct conditional_frame *cframe;
409{
410  memset (cframe, 0, sizeof (*cframe));
411  as_where (&cframe->if_file_line.file,
412	    &cframe->if_file_line.line);
413  cframe->previous_cframe = current_cframe;
414  cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring;
415  cframe->macro_nest = macro_nest;
416}
417
418/* Give an error if a conditional is unterminated inside a macro or
419   the assembly as a whole.  If NEST is non negative, we are being
420   called because of the end of a macro expansion.  If NEST is
421   negative, we are being called at the of the input files.  */
422
423void
424cond_finish_check (nest)
425     int nest;
426{
427  if (current_cframe != NULL && current_cframe->macro_nest >= nest)
428    {
429      as_bad ("end of %s inside conditional",
430	      nest >= 0 ? "macro" : "file");
431      as_bad_where (current_cframe->if_file_line.file,
432		    current_cframe->if_file_line.line,
433		    "here is the start of the unterminated conditional");
434      if (current_cframe->else_seen)
435	as_bad_where (current_cframe->else_file_line.file,
436		      current_cframe->else_file_line.line,
437		      "here is the \"else\" of the unterminated conditional");
438    }
439}
440
441/* This function is called when we exit out of a macro.  We assume
442   that any conditionals which began within the macro are correctly
443   nested, and just pop them off the stack.  */
444
445void
446cond_exit_macro (nest)
447     int nest;
448{
449  while (current_cframe != NULL && current_cframe->macro_nest >= nest)
450    {
451      struct conditional_frame *hold;
452
453      hold = current_cframe;
454      current_cframe = current_cframe->previous_cframe;
455      obstack_free (&cond_obstack, hold);
456    }
457}
458
459/* end of cond.c */
460