Copyright 1990, 1991, 1992, 1993, 1995, 1997, 1998, 2000, 2001, 2002,
2003, 2006, 2007 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to the Free
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
02110-1301, USA. */
#include "as.h"
#include "sb.h"
#include "macro.h"
#include "obstack.h"
scanned. */
struct obstack cond_obstack;
struct file_line {
char *file;
unsigned int line;
};
.endif. */
struct conditional_frame {
struct file_line if_file_line;
struct file_line else_file_line;
struct conditional_frame *previous_cframe;
int else_seen;
int ignoring;
Set also when a branch of an "if .. elseif .." tree has matched
to prevent further matches. */
int dead_tree;
int macro_nest;
};
static void initialize_cframe (struct conditional_frame *cframe);
static char *get_mri_string (int, int *);
static struct conditional_frame *current_cframe = NULL;
the .ifndef (test_defined == 0) pseudo op. */
void
s_ifdef (int test_defined)
{
char *name;
symbolS *symbolP;
struct conditional_frame cframe;
char c;
SKIP_WHITESPACE ();
name = input_line_pointer;
if (!is_name_beginner (*name))
{
as_bad (_("invalid identifier for \".ifdef\""));
obstack_1grow (&cond_obstack, 0);
ignore_rest_of_line ();
return;
}
c = get_symbol_end ();
symbolP = symbol_find (name);
*input_line_pointer = c;
initialize_cframe (&cframe);
if (cframe.dead_tree)
cframe.ignoring = 1;
else
{
int is_defined;
which has been referenced but not yet given a value/address is
considered to be undefined. */
is_defined =
symbolP != NULL
&& (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP))
&& S_GET_SEGMENT (symbolP) != reg_section;
cframe.ignoring = ! (test_defined ^ is_defined);
}
current_cframe = ((struct conditional_frame *)
obstack_copy (&cond_obstack, &cframe,
sizeof (cframe)));
if (LISTING_SKIP_COND ()
&& cframe.ignoring
&& (cframe.previous_cframe == NULL
|| ! cframe.previous_cframe->ignoring))
listing_list (2);
demand_empty_rest_of_line ();
}
void
s_if (int arg)
{
expressionS operand;
struct conditional_frame cframe;
int t;
char *stop = NULL;
char stopc;
if (flag_mri)
stop = mri_comment_field (&stopc);
SKIP_WHITESPACE ();
if (current_cframe != NULL && current_cframe->ignoring)
{
operand.X_add_number = 0;
while (! is_end_of_line[(unsigned char) *input_line_pointer])
++input_line_pointer;
}
else
{
expression_and_evaluate (&operand);
if (operand.X_op != O_constant)
as_bad (_("non-constant expression in \".if\" statement"));
}
switch ((operatorT) arg)
{
case O_eq: t = operand.X_add_number == 0; break;
case O_ne: t = operand.X_add_number != 0; break;
case O_lt: t = operand.X_add_number < 0; break;
case O_le: t = operand.X_add_number <= 0; break;
case O_ge: t = operand.X_add_number >= 0; break;
case O_gt: t = operand.X_add_number > 0; break;
default:
abort ();
return;
}
using an undefined result. No big deal. */
initialize_cframe (&cframe);
cframe.ignoring = cframe.dead_tree || ! t;
current_cframe = ((struct conditional_frame *)
obstack_copy (&cond_obstack, &cframe, sizeof (cframe)));
if (LISTING_SKIP_COND ()
&& cframe.ignoring
&& (cframe.previous_cframe == NULL
|| ! cframe.previous_cframe->ignoring))
listing_list (2);
if (flag_mri)
mri_comment_end (stop, stopc);
demand_empty_rest_of_line ();
}
the .ifnb (test_blank == 0) pseudo op. */
void
s_ifb (int test_blank)
{
struct conditional_frame cframe;
initialize_cframe (&cframe);
if (cframe.dead_tree)
cframe.ignoring = 1;
else
{
int is_eol;
SKIP_WHITESPACE ();
is_eol = is_end_of_line[(unsigned char) *input_line_pointer];
cframe.ignoring = (test_blank == !is_eol);
}
current_cframe = ((struct conditional_frame *)
obstack_copy (&cond_obstack, &cframe,
sizeof (cframe)));
if (LISTING_SKIP_COND ()
&& cframe.ignoring
&& (cframe.previous_cframe == NULL
|| ! cframe.previous_cframe->ignoring))
listing_list (2);
ignore_rest_of_line ();
}
static char *
get_mri_string (int terminator, int *len)
{
char *ret;
char *s;
SKIP_WHITESPACE ();
s = ret = input_line_pointer;
if (*input_line_pointer == '\'')
{
++s;
++input_line_pointer;
while (! is_end_of_line[(unsigned char) *input_line_pointer])
{
*s++ = *input_line_pointer++;
if (s[-1] == '\'')
{
if (*input_line_pointer != '\'')
break;
++input_line_pointer;
}
}
SKIP_WHITESPACE ();
}
else
{
while (*input_line_pointer != terminator
&& ! is_end_of_line[(unsigned char) *input_line_pointer])
++input_line_pointer;
s = input_line_pointer;
while (s > ret && (s[-1] == ' ' || s[-1] == '\t'))
--s;
}
*len = s - ret;
return ret;
}
void
s_ifc (int arg)
{
char *stop = NULL;
char stopc;
char *s1, *s2;
int len1, len2;
int res;
struct conditional_frame cframe;
if (flag_mri)
stop = mri_comment_field (&stopc);
s1 = get_mri_string (',', &len1);
if (*input_line_pointer != ',')
as_bad (_("bad format for ifc or ifnc"));
else
++input_line_pointer;
s2 = get_mri_string (';', &len2);
res = len1 == len2 && strncmp (s1, s2, len1) == 0;
initialize_cframe (&cframe);
cframe.ignoring = cframe.dead_tree || ! (res ^ arg);
current_cframe = ((struct conditional_frame *)
obstack_copy (&cond_obstack, &cframe, sizeof (cframe)));
if (LISTING_SKIP_COND ()
&& cframe.ignoring
&& (cframe.previous_cframe == NULL
|| ! cframe.previous_cframe->ignoring))
listing_list (2);
if (flag_mri)
mri_comment_end (stop, stopc);
demand_empty_rest_of_line ();
}
void
s_elseif (int arg)
{
if (current_cframe == NULL)
{
as_bad (_("\".elseif\" without matching \".if\""));
}
else if (current_cframe->else_seen)
{
as_bad (_("\".elseif\" after \".else\""));
as_bad_where (current_cframe->else_file_line.file,
current_cframe->else_file_line.line,
_("here is the previous \"else\""));
as_bad_where (current_cframe->if_file_line.file,
current_cframe->if_file_line.line,
_("here is the previous \"if\""));
}
else
{
as_where (¤t_cframe->else_file_line.file,
¤t_cframe->else_file_line.line);
current_cframe->dead_tree |= !current_cframe->ignoring;
current_cframe->ignoring = current_cframe->dead_tree;
}
if (current_cframe == NULL || current_cframe->ignoring)
{
while (! is_end_of_line[(unsigned char) *input_line_pointer])
++input_line_pointer;
if (current_cframe == NULL)
return;
}
else
{
expressionS operand;
int t;
SKIP_WHITESPACE ();
expression_and_evaluate (&operand);
if (operand.X_op != O_constant)
as_bad (_("non-constant expression in \".elseif\" statement"));
switch ((operatorT) arg)
{
case O_eq: t = operand.X_add_number == 0; break;
case O_ne: t = operand.X_add_number != 0; break;
case O_lt: t = operand.X_add_number < 0; break;
case O_le: t = operand.X_add_number <= 0; break;
case O_ge: t = operand.X_add_number >= 0; break;
case O_gt: t = operand.X_add_number > 0; break;
default:
abort ();
return;
}
current_cframe->ignoring = current_cframe->dead_tree || ! t;
}
if (LISTING_SKIP_COND ()
&& (current_cframe->previous_cframe == NULL
|| ! current_cframe->previous_cframe->ignoring))
{
if (! current_cframe->ignoring)
listing_list (1);
else
listing_list (2);
}
demand_empty_rest_of_line ();
}
void
s_endif (int arg ATTRIBUTE_UNUSED)
{
struct conditional_frame *hold;
if (current_cframe == NULL)
{
as_bad (_("\".endif\" without \".if\""));
}
else
{
if (LISTING_SKIP_COND ()
&& current_cframe->ignoring
&& (current_cframe->previous_cframe == NULL
|| ! current_cframe->previous_cframe->ignoring))
listing_list (1);
hold = current_cframe;
current_cframe = current_cframe->previous_cframe;
obstack_free (&cond_obstack, hold);
}
if (flag_mri)
{
while (! is_end_of_line[(unsigned char) *input_line_pointer])
++input_line_pointer;
}
demand_empty_rest_of_line ();
}
void
s_else (int arg ATTRIBUTE_UNUSED)
{
if (current_cframe == NULL)
{
as_bad (_("\".else\" without matching \".if\""));
}
else if (current_cframe->else_seen)
{
as_bad (_("duplicate \"else\""));
as_bad_where (current_cframe->else_file_line.file,
current_cframe->else_file_line.line,
_("here is the previous \"else\""));
as_bad_where (current_cframe->if_file_line.file,
current_cframe->if_file_line.line,
_("here is the previous \"if\""));
}
else
{
as_where (¤t_cframe->else_file_line.file,
¤t_cframe->else_file_line.line);
current_cframe->ignoring =
current_cframe->dead_tree | !current_cframe->ignoring;
if (LISTING_SKIP_COND ()
&& (current_cframe->previous_cframe == NULL
|| ! current_cframe->previous_cframe->ignoring))
{
if (! current_cframe->ignoring)
listing_list (1);
else
listing_list (2);
}
current_cframe->else_seen = 1;
}
if (flag_mri)
{
while (! is_end_of_line[(unsigned char) *input_line_pointer])
++input_line_pointer;
}
demand_empty_rest_of_line ();
}
void
s_ifeqs (int arg)
{
char *s1, *s2;
int len1, len2;
int res;
struct conditional_frame cframe;
s1 = demand_copy_C_string (&len1);
SKIP_WHITESPACE ();
if (*input_line_pointer != ',')
{
as_bad (_(".ifeqs syntax error"));
ignore_rest_of_line ();
return;
}
++input_line_pointer;
s2 = demand_copy_C_string (&len2);
res = len1 == len2 && strncmp (s1, s2, len1) == 0;
initialize_cframe (&cframe);
cframe.ignoring = cframe.dead_tree || ! (res ^ arg);
current_cframe = ((struct conditional_frame *)
obstack_copy (&cond_obstack, &cframe, sizeof (cframe)));
if (LISTING_SKIP_COND ()
&& cframe.ignoring
&& (cframe.previous_cframe == NULL
|| ! cframe.previous_cframe->ignoring))
listing_list (2);
demand_empty_rest_of_line ();
}
int
ignore_input (void)
{
char *s;
s = input_line_pointer;
if (NO_PSEUDO_DOT || flag_m68k_mri)
{
if (s[-1] != '.')
--s;
}
else
{
if (s[-1] != '.')
return (current_cframe != NULL) && (current_cframe->ignoring);
}
if (((s[0] == 'i'
|| s[0] == 'I')
&& (!strncasecmp (s, "if", 2)
|| !strncasecmp (s, "ifdef", 5)
|| !strncasecmp (s, "ifndef", 6)))
|| ((s[0] == 'e'
|| s[0] == 'E')
&& (!strncasecmp (s, "else", 4)
|| !strncasecmp (s, "endif", 5)
|| !strncasecmp (s, "endc", 4))))
return 0;
return (current_cframe != NULL) && (current_cframe->ignoring);
}
static void
initialize_cframe (struct conditional_frame *cframe)
{
memset (cframe, 0, sizeof (*cframe));
as_where (&cframe->if_file_line.file,
&cframe->if_file_line.line);
cframe->previous_cframe = current_cframe;
cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring;
cframe->macro_nest = macro_nest;
}
the assembly as a whole. If NEST is non negative, we are being
called because of the end of a macro expansion. If NEST is
negative, we are being called at the of the input files. */
void
cond_finish_check (int nest)
{
if (current_cframe != NULL && current_cframe->macro_nest >= nest)
{
if (nest >= 0)
as_bad (_("end of macro inside conditional"));
else
as_bad (_("end of file inside conditional"));
as_bad_where (current_cframe->if_file_line.file,
current_cframe->if_file_line.line,
_("here is the start of the unterminated conditional"));
if (current_cframe->else_seen)
as_bad_where (current_cframe->else_file_line.file,
current_cframe->else_file_line.line,
_("here is the \"else\" of the unterminated conditional"));
}
}
that any conditionals which began within the macro are correctly
nested, and just pop them off the stack. */
void
cond_exit_macro (int nest)
{
while (current_cframe != NULL && current_cframe->macro_nest >= nest)
{
struct conditional_frame *hold;
hold = current_cframe;
current_cframe = current_cframe->previous_cframe;
obstack_free (&cond_obstack, hold);
}
}