+/* This is allocated to grow and shrink as .ifdef/.endif pairs are
+ scanned. */
+struct obstack cond_obstack;
+
+struct file_line {
+ char *file;
+ unsigned int line;
+};
+
+/* We push one of these structures for each .if, and pop it at the
+ .endif. */
+
+struct conditional_frame {
+ /* The source file & line number of the "if". */
+ struct file_line if_file_line;
+ /* The source file & line of the "else". */
+ struct file_line else_file_line;
+ /* The previous conditional. */
+ struct conditional_frame *previous_cframe;
+ /* Have we seen an else yet? */
+ int else_seen;
+ /* Whether we are currently ignoring input. */
+ int ignoring;
+ /* Whether a conditional at a higher level is ignoring input.
+ Set also when a branch of an "if .. elseif .." tree has matched
+ to prevent further matches. */
+ int dead_tree;
+ /* Macro nesting level at which this conditional was created. */
+ 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;
+
+/* Performs the .ifdef (test_defined == 1) and
+ the .ifndef (test_defined == 0) pseudo op. */
+
+void
+s_ifdef (int test_defined)
+{
+ /* Points to name of symbol. */
+ char *name;
+ /* Points to symbol. */
+ symbolS *symbolP;
+ struct conditional_frame cframe;
+ char c;
+
+ /* Leading whitespace is part of operand. */
+ 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;
+
+ /* Use the same definition of 'defined' as .equiv so that a symbol
+ which has been referenced but not yet given a value/address is
+ considered to be undefined. */
+ is_defined =
+ symbolP != NULL
+ && S_IS_DEFINED (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);
+
+ /* Leading whitespace is part of operand. */
+ 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 (&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;
+ }
+
+ /* If the above error is signaled, this will dispatch
+ 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 ();
+}
+
+/* Get a string for the MRI IFC or IFNC pseudo-ops. */
+
+static char *
+get_mri_string (int terminator, int *len)