/*
 * Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *    		 Crown Copyright (c) 1997
 *    
 *    This TenDRA(r) Computer Program is subject to Copyright
 *    owned by the United Kingdom Secretary of State for Defence
 *    acting through the Defence Evaluation and Research Agency
 *    (DERA).  It is made available to Recipients with a
 *    royalty-free licence for its use, reproduction, transfer
 *    to other parties and amendment for any purpose not excluding
 *    product development provided that any such use et cetera
 *    shall be deemed to be acceptance of the following conditions:-
 *    
 *        (1) Its Recipients shall ensure that this Notice is
 *        reproduced upon any copies or amended versions of it;
 *    
 *        (2) Any amended version of it shall be clearly marked to
 *        show both the nature of and the organisation responsible
 *        for the relevant amendment or amendments;
 *    
 *        (3) Its onward transfer from a recipient to another
 *        party shall be deemed to be that party's acceptance of
 *        these conditions;
 *    
 *        (4) DERA gives no warranty or assurance as to its
 *        quality or suitability for any purpose and DERA accepts
 *        no liability whatsoever in relation to any use to which
 *        it may be put.
 *
 * $TenDRA: tendra/src/producers/common/utility/debug.c,v 1.14 2004/08/15 11:13:37 bp Exp $
 */


#include "config.h"
#include "producer.h"

#include "msgcat.h"

#include "c_types.h"
#include "exp_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "inst_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "off_ops.h"
#include "type_ops.h"
#include "virt_ops.h"
#include "error.h"
#include "catalog.h"
#include "buffer.h"
#include "capsule.h"
#include "char.h"
#include "debug.h"
#include "encode.h"
#include "file.h"
#include "hash.h"
#include "label.h"
#include "lex.h"
#include "mangle.h"
#include "operator.h"
#include "option.h"
#include "print.h"
#include "syntax.h"
#include "unmangle.h"
#include "ustring.h"
#ifdef RUNTIME


/*
 *    EXTENDED DEBUGGING FLAG
 *
 *    This flag enables the extended debug printing routines.
 */

int debugging = 0;


/*
 *    EXTENDED OFFSET PRINTING
 *
 *    This routine deals with those cases in print_offset which are only
 *    output in debugging mode.
 */

int
print_offset_aux(OFFSET off, BUFFER *bf, int sp)
{
    if (!IS_NULL_off (off)) {
		switch (TAG_off (off)) {
	    case off_type_tag : {
			TYPE t = DEREF_type (off_type_type (off));
			sp = print_type (t, bf, sp);
			break;
	    }
	    default : {
			sp = print_lex (lex_member_Hcap, bf, sp);
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    PRINT A UNARY OPERATION
 *
 *    This routine prints the unary operation 'op a' to the buffer bf.
 */

static int
print_unary(EXP a, int op, BUFFER *bf, int sp)
{
    IGNORE print_lex (op, bf, sp);
    sp = print_exp (a, 1, bf, 0);
    return (sp);
}


/*
 *    PRINT A BINARY OPERATION
 *
 *    This routine prints the binary operation 'a op b' to the buffer bf.
 */

static int
print_binary(EXP a, EXP b, int op, BUFFER *bf, int sp)
{
    sp = print_exp (a, 1, bf, sp);
    sp = print_lex (op, bf, sp);
    sp = print_exp (b, 1, bf, sp);
    return (sp);
}


/*
 *    PRINT A CAST OPERATION
 *
 *    This routine prints the cast operation 'op < t > (a)' to the
 *    buffer bf.
 */

static int
print_cast(TYPE t, EXP a, int op, BUFFER *bf, int sp)
{
    sp = print_lex (op, bf, sp);
    sp = print_lex (lex_less, bf, sp);
    sp = print_type (t, bf, sp);
    sp = print_lex (lex_greater, bf, sp);
    sp = print_exp (a, 1, bf, sp);
    return (sp);
}


/*
 *    PRINT A LIST OF EXPRESSIONS
 *
 *    This routine prints the list of expressions p, enclosed in brackets,
 *    to the buffer bf.
 */

static int
print_exp_list(LIST (EXP) p, BUFFER *bf, int sp)
{
    sp = print_lex (lex_open_Hround, bf, sp);
    if (IS_NULL_list (p)) {
		sp = 0;
    } else {
		while (!IS_NULL_list (p)) {
			EXP a = DEREF_exp (HEAD_list (p));
			sp = print_exp (a, 1, bf, sp);
			bfputc (bf, ',');
			p = TAIL_list (p);
		}
    }
    sp = print_lex (lex_close_Hround, bf, sp);
    return (sp);
}


/*
 *    EXTENDED EXPRESSION PRINTING
 *
 *    This routine deals with those cases in print_exp which are only
 *    output in debugging mode.
 */

int
print_exp_aux(EXP e, int paren, BUFFER *bf, int sp)
{
    if (!IS_NULL_exp (e)) {
		ASSERT (ORDER_exp == 88);
		if (paren) sp = print_lex (lex_open_Hround, bf, sp);
		switch (TAG_exp (e)) {
	    case exp_paren_tag : {
			EXP a = DEREF_exp (exp_paren_arg (e));
			sp = print_exp (a, !paren, bf, sp);
			break;
	    }
	    case exp_copy_tag : {
			EXP a = DEREF_exp (exp_copy_arg (e));
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_assign_tag : {
			EXP a = DEREF_exp (exp_assign_ref (e));
			EXP b = DEREF_exp (exp_assign_arg (e));
			sp = print_binary (a, b, lex_assign, bf, sp);
			break;
	    }
	    case exp_init_tag : {
			IDENTIFIER id = DEREF_id (exp_init_id (e));
			EXP a = DEREF_exp (exp_init_arg (e));
			sp = print_id_short (id, qual_none, bf, sp);
			sp = print_lex (lex_assign, bf, sp);
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_preinc_tag : {
			EXP a = DEREF_exp (exp_preinc_ref (e));
			EXP b = DEREF_exp (exp_preinc_op (e));
			sp = print_binary (a, b, lex_assign, bf, sp);
			break;
	    }
	    case exp_postinc_tag : {
			EXP a = DEREF_exp (exp_postinc_ref (e));
			EXP b = DEREF_exp (exp_postinc_op (e));
			sp = print_binary (a, b, lex_assign, bf, sp);
			break;
	    }
	    case exp_indir_tag : {
			EXP a = DEREF_exp (exp_indir_ptr (e));
			sp = print_unary (a, lex_star, bf, sp);
			break;
	    }
	    case exp_address_tag : {
			EXP a = DEREF_exp (exp_address_arg (e));
			sp = print_unary (a, lex_and_H1, bf, sp);
			break;
	    }
	    case exp_address_mem_tag : {
			EXP a = DEREF_exp (exp_address_mem_arg (e));
			sp = print_unary (a, lex_and_H1, bf, sp);
			break;
	    }
	    case exp_func_tag : {
			EXP a = DEREF_exp (exp_func_fn (e));
			LIST (EXP) p = DEREF_list (exp_func_args (e));
			sp = print_exp (a, 1, bf, sp);
			sp = print_exp_list (p, bf, sp);
			break;
	    }
	    case exp_func_id_tag : {
			IDENTIFIER id = DEREF_id (exp_func_id_id (e));
			LIST (EXP) p = DEREF_list (exp_func_id_args (e));
			sp = print_id_short (id, qual_none, bf, sp);
			sp = print_exp_list (p, bf, sp);
			break;
	    }
	    case exp_call_tag : {
			EXP a = DEREF_exp (exp_call_ptr (e));
			EXP b = DEREF_exp (exp_call_arg (e));
			sp = print_binary (a, b, lex_dot_Hstar, bf, sp);
			break;
	    }
	    case exp_negate_tag :
	    case exp_compl_tag :
	    case exp_not_tag :
	    case exp_abs_tag : {
			int op = op_token (e, lex_unknown);
			EXP a = DEREF_exp (exp_negate_etc_arg (e));
			sp = print_unary (a, op, bf, sp);
			break;
	    }
	    case exp_plus_tag :
	    case exp_minus_tag :
	    case exp_mult_tag :
	    case exp_div_tag :
	    case exp_rem_tag :
	    case exp_and_tag :
	    case exp_or_tag :
	    case exp_xor_tag :
	    case exp_log_and_tag :
	    case exp_log_or_tag :
	    case exp_lshift_tag :
	    case exp_rshift_tag :
	    case exp_max_tag :
	    case exp_min_tag : {
			int op = op_token (e, lex_unknown);
			EXP a = DEREF_exp (exp_plus_etc_arg1 (e));
			EXP b = DEREF_exp (exp_plus_etc_arg2 (e));
			sp = print_binary (a, b, op, bf, sp);
			break;
	    }
	    case exp_test_tag : {
			int op = op_token (e, lex_unknown);
			EXP a = DEREF_exp (exp_test_arg (e));
			sp = print_exp (a, 1, bf, sp);
			IGNORE print_lex (op, bf, sp);
			bfprintf (bf, " 0");
			sp = 1;
			break;
	    }
	    case exp_compare_tag : {
			int op = op_token (e, lex_unknown);
			EXP a = DEREF_exp (exp_compare_arg1 (e));
			EXP b = DEREF_exp (exp_compare_arg2 (e));
			sp = print_binary (a, b, op, bf, sp);
			break;
	    }
	    case exp_cast_tag : {
			TYPE t = DEREF_type (exp_type (e));
			EXP a = DEREF_exp (exp_cast_arg (e));
			sp = print_cast (t, a, lex_cast, bf, sp);
			break;
	    }
	    case exp_base_cast_tag : {
			TYPE t = DEREF_type (exp_type (e));
			EXP a = DEREF_exp (exp_base_cast_arg (e));
			sp = print_cast (t, a, lex_cast, bf, sp);
			break;
	    }
	    case exp_dyn_cast_tag : {
			TYPE t = DEREF_type (exp_type (e));
			EXP a = DEREF_exp (exp_dyn_cast_arg (e));
			sp = print_cast (t, a, lex_dynamic_Hcast, bf, sp);
			break;
	    }
	    case exp_add_ptr_tag : {
			EXP a = DEREF_exp (exp_add_ptr_ptr (e));
			OFFSET off = DEREF_off (exp_add_ptr_off (e));
			sp = print_exp (a, 0, bf, sp);
			sp = print_lex (lex_plus, bf, sp);
			sp = print_offset (off, bf, sp);
			break;
	    }
	    case exp_offset_size_tag : {
			OFFSET off = DEREF_off (exp_offset_size_off (e));
			TYPE t = DEREF_type (exp_offset_size_step (e));
			sp = print_offset (off, bf, sp);
			sp = print_lex (lex_div, bf, sp);
			sp = print_type (t, bf, sp);
			break;
	    }
	    case exp_constr_tag : {
			EXP a = DEREF_exp (exp_constr_call (e));
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_destr_tag : {
			EXP a = DEREF_exp (exp_destr_call (e));
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_alloc_tag : {
			EXP a = DEREF_exp (exp_alloc_call (e));
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_dealloc_tag : {
			EXP a = DEREF_exp (exp_dealloc_call (e));
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_rtti_tag : {
			EXP a = DEREF_exp (exp_rtti_arg (e));
			int op = DEREF_int (exp_rtti_op (e));
			sp = print_lex (op, bf, sp);
			sp = print_exp (a, 1, bf, sp);
			break;
	    }
	    case exp_rtti_type_tag : {
			TYPE t = DEREF_type (exp_rtti_type_arg (e));
			int op = DEREF_int (exp_rtti_type_op (e));
			sp = print_lex (op, bf, sp);
			sp = print_type (t, bf, sp);
			break;
	    }
	    case exp_rtti_no_tag : {
			TYPE t = DEREF_type (exp_rtti_no_arg (e));
			sp = print_lex (lex_typeid, bf, sp);
			sp = print_type (t, bf, sp);
			break;
	    }
	    case exp_dynamic_tag : {
			EXP a = DEREF_exp (exp_dynamic_arg (e));
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_aggregate_tag : {
			LIST (EXP) p = DEREF_list (exp_aggregate_args (e));
			sp = print_lex (lex_initialization, bf, sp);
			sp = print_exp_list (p, bf, sp);
			break;
	    }
	    case exp_initialiser_tag : {
			LIST (EXP) p = DEREF_list (exp_initialiser_args (e));
			sp = print_lex (lex_initialization, bf, sp);
			sp = print_exp_list (p, bf, sp);
			break;
	    }
	    case exp_nof_tag : {
			EXP a = DEREF_exp (exp_nof_start (e));
			EXP b = DEREF_exp (exp_nof_pad (e));
			EXP c = DEREF_exp (exp_nof_end (e));
			if (!IS_NULL_exp (a)) {
				sp = print_exp (a, 0, bf, sp);
				bfprintf (bf, ", ");
			}
			sp = print_exp (b, 0, bf, sp);
			bfprintf (bf, ", ...");
			if (!IS_NULL_exp (c)) {
				bfprintf (bf, ", ");
				sp = print_exp (c, 0, bf, sp);
			}
			break;
	    }
	    case exp_comma_tag : {
			LIST (EXP) p = DEREF_list (exp_comma_args (e));
			sp = print_exp_list (p, bf, sp);
			break;
	    }
	    case exp_set_tag : {
			EXP a = DEREF_exp (exp_set_arg (e));
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_unused_tag : {
			EXP a = DEREF_exp (exp_unused_arg (e));
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_if_stmt_tag : {
			EXP c = DEREF_exp (exp_if_stmt_cond (e));
			EXP a = DEREF_exp (exp_if_stmt_true_code (e));
			EXP b = DEREF_exp (exp_if_stmt_false_code (e));
			sp = print_exp (c, 1, bf, sp);
			sp = print_lex (lex_question, bf, sp);
			sp = print_binary (a, b, lex_colon, bf, sp);
			break;
	    }
	    case exp_exception_tag : {
			EXP a = DEREF_exp (exp_exception_arg (e));
			sp = print_lex (lex_throw, bf, sp);
			sp = print_exp (a, 1, bf, sp);
			break;
	    }
	    case exp_thrown_tag : {
			sp = print_lex (lex_catch, bf, sp);
			break;
	    }
	    case exp_op_tag : {
			int op = DEREF_int (exp_op_lex (e));
			EXP a = DEREF_exp (exp_op_arg1 (e));
			EXP b = DEREF_exp (exp_op_arg2 (e));
			if (IS_NULL_exp (b)) {
				sp = print_unary (a, op, bf, sp);
			} else {
				sp = print_binary (a, b, op, bf, sp);
			}
			break;
	    }
	    case exp_opn_tag : {
			int op = DEREF_int (exp_opn_lex (e));
			LIST (EXP) p = DEREF_list (exp_opn_args (e));
			sp = print_lex (op, bf, sp);
			sp = print_exp_list (p, bf, sp);
			break;
	    }
	    case exp_uncompiled_tag : {
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "...");
			sp = 1;
			break;
	    }
	    case exp_fail_tag : {
			string s = DEREF_string (exp_fail_msg (e));
			if (sp) bfputc (bf, ' ');
			bfputs (bf, s);
			sp = 1;
			break;
	    }
	    case exp_dummy_tag : {
			EXP a = DEREF_exp (exp_dummy_value (e));
			if (IS_NULL_exp (a)) {
				sp = print_lex (lex_exp_Hcap, bf, sp);
			} else {
				sp = print_exp (a, 0, bf, sp);
			}
			break;
	    }
	    default : {
			sp = print_lex (lex_exp_Hcap, bf, sp);
			break;
	    }
		}
		if (paren) sp = print_lex (lex_close_Hround, bf, sp);
    }
    return (sp);
}


/*
 *    PRINT AN INDENTED STRING
 *
 *    This routine prints an indentation of indent steps followed by the
 *    string text to the file f.
 */

static void
print_indent(int indent, const char *text, FILE *f)
{
    while (indent > 1) {
		fputc_v ('\t', f);
		indent -= 2;
    }
    if (indent) {
		unsigned long i = tab_width / 2;
		while (i) {
			fputc_v (' ', f);
			i--;
		}
    }
    fputs_v (text, f);
    return;
}


/*
 *    PRINT AN EXPRESSION
 *
 *    This routine prints the expression e, enclosed in parentheses if paren
 *    is true and preceded by a space if sp is true, to the file f.
 */

static void
print_expr(EXP e, int paren, int sp, FILE *f)
{
    BUFFER *bf = clear_buffer (&print_buff, f);
    if (paren) sp = print_lex (lex_open_Hround, bf, sp);
    IGNORE print_exp (e, 0, bf, sp);
    if (paren) IGNORE print_lex (lex_close_Hround, bf, sp);
    output_buffer (bf, 1);
    return;
}


/*
 *    PRINT AN INTEGER CONSTANT
 *
 *    This routine prints the integer constant n to the file f.
 */

static void
print_nat_val(NAT n, FILE *f)
{
    BUFFER *bf = clear_buffer (&print_buff, f);
    IGNORE print_nat (n, 0, bf, 0);
    output_buffer (bf, 1);
    return;
}


/*
 *    PRINT A DECLARATION
 *
 *    This routine prints the declaration id to the file f.
 */

static void
print_decl(IDENTIFIER id, FILE *f)
{
    EXP e;
    BUFFER *bf = clear_buffer (&print_buff, f);
    print_id_desc++;
    IGNORE print_id_long (id, qual_none, bf, 0);
    print_id_desc--;
    e = DEREF_exp (id_variable_init (id));
    if (!IS_NULL_exp (e)) {
		bfprintf (bf, " = ");
		IGNORE print_exp (e, 0, bf, 0);
    }
    output_buffer (bf, 1);
    return;
}


/*
 *    PRINT A LABEL
 *
 *    This routine prints the label lab to the file f.
 */

static void
print_label(IDENTIFIER lab, FILE *f)
{
    int op = DEREF_int (id_label_op (lab));
    if (op == lex_identifier) {
		HASHID nm = DEREF_hashid (id_name (lab));
		if (IS_hashid_name_etc (nm)) {
			string s = DEREF_string (hashid_name_etc_text (nm));
			fputs_v (strlit (s), f);
		} else {
			fputs_v ("????", f);
		}
    } else if (op == lex_case) {
		NAT n = find_case_nat (lab);
		fputs_v ("case ", f);
		print_nat_val (n, f);
    } else {
		fputs_v (token_names [ op ], f);
    }
    return;
}


/*
 *    PRINT A STATEMENT
 *
 *    This routine prints the statement e at an indentation of indent to the
 *    file f.  block is false to suppress braces around compound statements.
 */

static void
print_stmt(EXP e, int indent, int block, FILE *f)
{
    if (IS_NULL_exp (e)) {
		/* Empty statements */
		print_indent (indent, ";\n", f);
		return;
    }
    ASSERT (ORDER_exp == 88);
    switch (TAG_exp (e)) {
	case exp_sequence_tag : {
	    /* Compound statements */
	    LIST (EXP) p = DEREF_list (exp_sequence_first (e));
	    p = TAIL_list (p);
	    if (block) print_indent (indent, "{\n", f);
	    while (!IS_NULL_list (p)) {
			EXP a = DEREF_exp (HEAD_list (p));
			print_stmt (a, indent + block, 1, f);
			p = TAIL_list (p);
	    }
	    if (block) print_indent (indent, "}\n", f);
	    break;
	}
	case exp_solve_stmt_tag : {
	    /* Solve statements */
	    EXP a = DEREF_exp (exp_solve_stmt_body (e));
	    print_stmt (a, indent, block, f);
	    break;
	}
	case exp_decl_stmt_tag : {
	    /* Declaration statements */
	    EXP a = DEREF_exp (exp_decl_stmt_body (e));
	    IDENTIFIER id = DEREF_id (exp_decl_stmt_id (e));
	    print_indent (indent, "", f);
	    print_decl (id, f);
	    fputs_v (" : {\n", f);
	    print_stmt (a, indent + 1, 0, f);
	    print_indent (indent, "}\n", f);
	    break;
	}
	case exp_if_stmt_tag : {
	    /* Conditional statements */
	    EXP c = DEREF_exp (exp_if_stmt_cond (e));
	    EXP a = DEREF_exp (exp_if_stmt_true_code (e));
	    EXP b = DEREF_exp (exp_if_stmt_false_code (e));
	    print_indent (indent, "if ", f);
	    print_expr (c, 1, 0, f);
	    fputs_v (" {\n", f);
	    print_stmt (a, indent + 1, 0, f);
	    if (!IS_NULL_exp (b)) {
			print_indent (indent, "} else {\n", f);
			print_stmt (b, indent + 1, 0, f);
	    }
	    print_indent (indent, "}\n", f);
	    break;
	}
	case exp_while_stmt_tag : {
	    /* While statements */
	    EXP c = DEREF_exp (exp_while_stmt_cond (e));
	    EXP a = DEREF_exp (exp_while_stmt_body (e));
	    IDENTIFIER blab = DEREF_id (exp_while_stmt_break_lab (e));
	    IDENTIFIER clab = DEREF_id (exp_while_stmt_cont_lab (e));
	    EXP bs = DEREF_exp (id_label_stmt (blab));
	    EXP cs = DEREF_exp (id_label_stmt (clab));
	    print_indent (indent, "while ", f);
	    print_expr (c, 1, 0, f);
	    fputs_v (" {\n", f);
	    print_stmt (a, indent + 1, 0, f);
	    print_stmt (cs, indent + 1, 0, f);
	    print_indent (indent, "}\n", f);
	    print_stmt (bs, indent, 0, f);
	    break;
	}
	case exp_do_stmt_tag : {
	    /* Do statements */
	    EXP c = DEREF_exp (exp_do_stmt_cond (e));
	    EXP a = DEREF_exp (exp_do_stmt_body (e));
	    IDENTIFIER blab = DEREF_id (exp_do_stmt_break_lab (e));
	    IDENTIFIER clab = DEREF_id (exp_do_stmt_cont_lab (e));
	    EXP bs = DEREF_exp (id_label_stmt (blab));
	    EXP cs = DEREF_exp (id_label_stmt (clab));
	    print_indent (indent, "do {\n", f);
	    print_stmt (a, indent + 1, 0, f);
	    print_stmt (cs, indent + 1, 0, f);
	    print_indent (indent, "} while ", f);
	    print_expr (c, 1, 0, f);
	    fputs_v (" ;\n", f);
	    print_stmt (bs, indent, 0, f);
	    break;
	}
	case exp_switch_stmt_tag : {
	    /* Switch statements */
	    EXP c = DEREF_exp (exp_switch_stmt_control (e));
	    EXP a = DEREF_exp (exp_switch_stmt_body (e));
	    IDENTIFIER blab = DEREF_id (exp_switch_stmt_break_lab (e));
	    EXP bs = DEREF_exp (id_label_stmt (blab));
	    print_indent (indent, "switch ", f);
	    print_expr (c, 1, 0, f);
	    fputs_v (" {\n", f);
	    print_stmt (a, indent + 1, 0, f);
	    print_indent (indent, "}\n", f);
	    print_stmt (bs, indent, 0, f);
	    break;
	}
	case exp_hash_if_tag : {
	    /* Target dependent conditional statements */
	    EXP c = DEREF_exp (exp_hash_if_cond (e));
	    EXP a = DEREF_exp (exp_hash_if_true_code (e));
	    EXP b = DEREF_exp (exp_hash_if_false_code (e));
	    fputs_v ("#if ", f);
	    print_expr (c, 0, 0, f);
	    fputs_v ("\n", f);
	    print_stmt (a, indent, 0, f);
	    if (!IS_NULL_exp (b)) {
			fputs_v ("#else\n", f);
			print_stmt (b, indent, 0, f);
	    }
	    fputs_v ("#endif\n", f);
	    break;
	}
	case exp_return_stmt_tag : {
	    /* Return statements */
	    EXP a = DEREF_exp (exp_return_stmt_value (e));
	    print_indent (indent, "return", f);
	    if (!IS_NULL_exp (a)) print_expr (a, 1, 1, f);
	    fputs_v (" ;\n", f);
	    break;
	}
	case exp_goto_stmt_tag : {
	    /* Goto statements */
	    IDENTIFIER lab = DEREF_id (exp_goto_stmt_label (e));
	    print_indent (indent, "goto ", f);
	    print_label (lab, f);
	    fputs_v (" ;\n", f);
	    break;
	}
	case exp_label_stmt_tag : {
	    /* Labelled statements */
	    EXP a = DEREF_exp (exp_label_stmt_body (e));
	    IDENTIFIER lab = DEREF_id (exp_label_stmt_label (e));
	    IDENTIFIER nlab = DEREF_id (exp_label_stmt_next (e));
	    print_indent (indent, "", f);
	    print_label (lab, f);
	    fputs_v (" : {\n", f);
	    print_stmt (a, indent + 1, 0, f);
	    if (!IS_NULL_id (nlab)) {
			print_indent (indent + 1, "goto ", f);
			print_label (nlab, f);
			fputs_v (" ;\n", f);
	    }
	    print_indent (indent, "}\n", f);
	    break;
	}
	case exp_try_block_tag : {
	    /* Try blocks */
	    EXP a = DEREF_exp (exp_try_block_body (e));
	    LIST (EXP) p = DEREF_list (exp_try_block_handlers (e));
	    EXP b = DEREF_exp (exp_try_block_ellipsis (e));
	    print_indent (indent, "try {\n", f);
	    print_stmt (a, indent + 1, 0, f);
	    print_indent (indent, "}\n", f);
	    while (!IS_NULL_list (p)) {
			EXP c = DEREF_exp (HEAD_list (p));
			print_stmt (c, indent + 1, 0, f);
			p = TAIL_list (p);
	    }
	    if (!IS_NULL_exp (b)) {
			print_indent (indent, "catch (...) {\n", f);
			print_stmt (b, indent + 1, 0, f);
			print_indent (indent, "}\n", f);
	    }
	    break;
	}
	case exp_handler_tag : {
	    /* Exception handlers */
	    IDENTIFIER id = DEREF_id (exp_handler_except (e));
	    EXP a = DEREF_exp (exp_handler_body (e));
	    print_indent (indent, "catch (", f);
	    if (!IS_NULL_id (id)) print_decl (id, f);
	    fputs_v (") {\n", f);
	    print_stmt (a, indent + 1, 0, f);
	    print_indent (indent, "}\n", f);
	    break;
	}
	case exp_reach_tag :
	case exp_unreach_tag : {
	    /* Reached statements */
	    EXP a = DEREF_exp (exp_reach_etc_body (e));
	    print_stmt (a, indent, block, f);
	    break;
	}
	case exp_location_tag : {
	    /* Location statements */
	    EXP a = DEREF_exp (exp_location_arg (e));
	    print_stmt (a, indent, block, f);
	    break;
	}
	default : {
	    /* Expression statements */
	    print_indent (indent, "", f);
	    print_expr (e, 0, 0, f);
	    fputs_v (" ;\n", f);
	    break;
	}
    }
    return;
}


/*
 *    PRINT A BITMASK TYPE
 *
 *    This routine prints the bitmask value n using the attribute names
 *    given by s.
 */

static void
print_bitmask(unsigned long n, const char **s)
{
    int sp = 0;
    FILE *f = DEBUG_file;
    if (n) {
		int i;
		unsigned long m = 1;
		for (i = 0; i < 32; i++) {
			if (n & m) {
				const char *c = s [i];
				if (c) {
					if (sp) fputs_v (" | ", f);
					fputs_v (c, f);
					sp = 1;
				}
			}
			m <<= 1;
		}
    }
    if (!sp) fputs_v ("none", f);
    fputc_v ('\n', f);
    fflush_v (f);
    return;
}


/*
 *    PRINT A BITSTREAM
 *
 *    This routine prints the contents of the bitstream bs to the file f.
 */

static void
print_bitstream(BITSTREAM *bs, FILE *f)
{
    if (bs) {
		string s = bs->text;
		unsigned i = bs->bits;
		unsigned n = bs->bytes;
		print_bitstream (bs->prev, f);
		fprintf_v (f, "0x%p = { ", (void *)bs);
		while (n) {
			unsigned j;
			unsigned c = (unsigned) *s;
			for (j = 0; j < 8; j++) {
				fputc_v (((c & 0x80) ? '1' : '0'), f);
				c <<= 1;
			}
			fputc_v (' ', f);
			n--;
			s++;
		}
		if (i) {
			unsigned j;
			unsigned c = (unsigned) *s;
			for (j = 0; j < i; j++) {
				fputc_v (((c & 0x80) ? '1' : '0'), f);
				c <<= 1;
			}
		}
		fputs_v (" }\n", f);
    }
    return;
}


/*
 *    TYPE DEBUGGING ROUTINES
 *
 *    These routines are used during debugging for printing objects of various
 *    types.
 */

void
DEBUG_access(DECL_SPEC ds)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_access (ds, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_bits(BITSTREAM *bs)
{
    FILE *f = DEBUG_file;
    print_bitstream (bs, f);
    fflush_v (f);
    return;
}

void
DEBUG_btype(BASE_TYPE bt)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_btype (bt, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_cinfo(CLASS_INFO ci)
{
    static const char *cinfos [32] = {
		/* Keep in line with c_class.alg */
		"complete", "defined", "struct", "union", "template", "token",
		"pod", "nested", "merge", "rescan", "recursive", "incomplete",
		"base", "multiple_base", "virtual_base", "templ_base", "ambiguous",
		"empty", "private", "static", "function", "params", "polymorphic",
		"poly_base", "abstract", "trivial_constr", "trivial_destr",
		"trivial_copy", "trivial_assign", "const_copy", "const_assign",
		"usr_constr"
    };
    print_bitmask ((unsigned long) ci, cinfos);
    return;
}

void
DEBUG_ctype(CLASS_TYPE ct)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_ctype (ct, qual_none, 0, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_cusage(CLASS_USAGE cu)
{
    static const char *cusages [32] = {
		/* Keep in line with c_class.alg */
		"address", "destr", "delete", "delete_array", NULL, NULL, NULL,
		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
		NULL, NULL, NULL
    };
    print_bitmask ((unsigned long) cu, cusages);
    return;
}

void
DEBUG_cv(CV_SPEC cv)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_cv (cv, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_dspec(DECL_SPEC ds)
{
    static const char *dspecs [32] = {
		/* Keep in step with c_class.alg */
		"used", "called", "defn", "inherit", "alias", "done", "static",
		"extern", "auto", "register", "mutable", "inline", "virtual",
		"explicit", "friend", "typedef", "public", "protected", "public2",
		"protected2", "c", "cpp", "ignore", "implicit", "instance",
		"main", "pure", "reserve", "temp", "template", "token", "trivial"
    };
    print_bitmask ((unsigned long) ds, dspecs);
    return;
}

void
DEBUG_etype(ENUM_TYPE et)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_etype (et, 0, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_exp(EXP e)
{
    FILE *f = DEBUG_file;
    debugging++;
    print_expr (e, 0, 0, f);
    debugging--;
    fputc_v ('\n', f);
    fflush_v (f);
    return;
}

void
DEBUG_flt(FLOAT f)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_flt (f, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_ftype(FLOAT_TYPE ft)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_ftype (ft, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_func(IDENTIFIER id)
{
    if (!IS_NULL_id (id)) {
		DEBUG_id_long (id);
		if (IS_id_function_etc (id)) {
			id = DEREF_id (id_function_etc_over (id));
			DEBUG_func (id);
		}
    }
    return;
}

void
DEBUG_graph(GRAPH gr)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_graph (gr, 0, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_hashid(HASHID nm)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_hashid (nm, 1, 1, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_hash_table(string s)
{
    unsigned long i = 0;
    unsigned long m = HASH_SIZE;
    if (s) {
		i = hash (s);
		m = i + 1;
		IGNORE lookup_name (s, i, 0, lex_unknown);
    }
    debugging++;
    while (i < m) {
		HASHID nm = hash_table [i];
		if (!IS_NULL_hashid (nm)) {
			BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
			bfprintf (bf, "%lu:", i);
			while (!IS_NULL_hashid (nm)) {
				IGNORE print_hashid (nm, 1, 1, bf, 1);
				nm = DEREF_hashid (hashid_next (nm));
			}
			bfputc (bf, '\n');
			output_buffer (bf, 1);
		}
		i++;
    }
    debugging--;
    return;
}

void
DEBUG_id(IDENTIFIER id)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_id_short (id, qual_none, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_id_long(IDENTIFIER id)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    print_id_desc++;
    IGNORE print_id_long (id, qual_none, bf, 0);
    print_id_desc--;
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_inst(INSTANCE inst)
{
    if (!IS_NULL_inst (inst)) {
		TYPE form = DEREF_type (inst_form (inst));
		DEBUG_type (form);
    }
    return;
}

void
DEBUG_insts(INSTANCE inst)
{
    while (!IS_NULL_inst (inst)) {
		DECL_SPEC acc = dspec_none;
		TYPE form = DEREF_type (inst_form (inst));
		if (IS_inst_templ (inst)) {
			acc = DEREF_dspec (inst_templ_access (inst));
		}
		DEBUG_dspec (acc);
		DEBUG_type (form);
		inst = DEREF_inst (inst_next (inst));
    }
    return;
}

void
DEBUG_itype(INT_TYPE it)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_itype (it, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_lex(int t)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_lex (t, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_loc(LOCATION *loc)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_loc (loc, NULL, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_mangle(IDENTIFIER id)
{
    FILE *f = DEBUG_file;
    const char *s = NULL;
    if (!IS_NULL_id (id)) {
		int v = VAR_tag;
		if (IS_id_token (id)) v = VAR_token;
		s = strlit (mangle_name (id, v, 0));
    }
    if (s == NULL) s = "(NULL)";
    fprintf_v (f, "%s\n", s);
    fflush_v (f);
    return;
}

void
DEBUG_member(MEMBER mem)
{
    if (!IS_NULL_member (mem)) {
		IDENTIFIER id = DEREF_id (member_id (mem));
		IDENTIFIER alt = DEREF_id (member_alt (mem));
		DEBUG_id_long (id);
		DEBUG_id_long (alt);
    }
    return;
}

void
DEBUG_members(NAMESPACE ns)
{
    if (!IS_NULL_nspace (ns)) {
		MEMBER mem;
		FILE *f = DEBUG_file;
		if (IS_nspace_named_etc (ns)) {
			mem = DEREF_member (nspace_named_etc_first (ns));
		} else {
			mem = DEREF_member (nspace_last (ns));
		}
		DEBUG_nspace (ns);
		fputs_v ("{\n", f);
		while (!IS_NULL_member (mem)) {
			IDENTIFIER id = DEREF_id (member_id (mem));
			IDENTIFIER alt = DEREF_id (member_alt (mem));
			if (!IS_NULL_id (id)) {
				fputs_v ("    ", f);
				DEBUG_id_long (id);
			}
			if (!IS_NULL_id (alt) && !EQ_id (id, alt)) {
				fputs_v ("    ", f);
				DEBUG_id_long (alt);
			}
			mem = DEREF_member (member_next (mem));
		}
		fputs_v ("}\n", f);
    }
    return;
}

void
DEBUG_nat(NAT n)
{
    FILE *f = DEBUG_file;
    debugging++;
    print_nat_val (n, f);
    debugging--;
    fputc_v ('\n', f);
    fflush_v (f);
    return;
}

void
DEBUG_nspace(NAMESPACE ns)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_nspace (ns, qual_none, 0, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_ntype(BUILTIN_TYPE nt)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_ntype (nt, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_offset(OFFSET off)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_offset (off, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_ntest(NTEST nt)
{
    int op = ntest_token (nt, lex_unknown);
    DEBUG_lex (op);
    return;
}

void
DEBUG_pptok(PPTOKEN *p)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_pptok (p, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_pptoks(PPTOKEN *p)
{
    while (p != NULL) {
		DEBUG_pptok (p);
		p = p->next;
    }
    return;
}

void
DEBUG_sort(TOKEN tok)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_sort (tok, 0, bf, 0);
    bfprintf (bf, " = ");
    IGNORE print_tok_value (tok, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_source(int lines)
{
    FILE *f = DEBUG_file;
    update_column ();
    print_source (&crt_loc, lines, 1, "", f);
    fflush_v (f);
    return;
}

void
DEBUG_stmt(EXP e)
{
    FILE *f = DEBUG_file;
    debugging++;
    print_stmt (e, 0, 1, f);
    debugging--;
    fflush_v (f);
    return;
}

void
DEBUG_str(STRING s)
{
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    IGNORE print_str (s, bf, 0);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_type(TYPE t)
{
    int sp = 0;
    BUFFER *bf = clear_buffer (&print_buff, DEBUG_file);
    debugging++;
    if (!IS_NULL_type (t)) {
		CV_SPEC cv = DEREF_cv (type_qual (t));
		if (cv & cv_lvalue) sp = print_lex (lex_lvalue, bf, sp);
    }
    IGNORE print_type (t, bf, sp);
    debugging--;
    bfputc (bf, '\n');
    output_buffer (bf, 1);
    return;
}

void
DEBUG_unmangle(const char *s)
{
    LIST (string) p = NULL_list (string);
    CONS_string (ustrlit (s), p, p);
    unmangle_list (p, DEBUG_file, 0);
    DESTROY_list (p, SIZE_string);
    return;
}

void
DEBUG_virt(VIRTUAL vt)
{
    if (!IS_NULL_virt (vt)) {
		if (IS_virt_table (vt)) {
			unsigned n = 1;
			LIST (VIRTUAL) vs = DEREF_list (virt_table_entries (vt));
			while (!IS_NULL_list (vs)) {
				vt = DEREF_virt (HEAD_list (vs));
				fprintf_v (DEBUG_file, "%u: ", n);
				DEBUG_virt (vt);
				n++;
				vs = TAIL_list (vs);
			}
		} else {
			IDENTIFIER fn = DEREF_id (virt_func (vt));
			DEBUG_id_long (fn);
		}
    }
    return;
}

void
DEBUG_where()
{
    update_column ();
    DEBUG_loc (&crt_loc);
    return;
}


/*
 *    GENERIC TYPE DEBUGGING ROUTINE
 *
 *    This routine is a generic debugging routine for printing any c_class
 *    object.  It relies on run-time type information to determine the
 *    static type of p.
 */

#if c_class_IMPLEMENTATION

void
DEBUG_c_class(c_class *p, int indent)
{
    FILE *f = DEBUG_file;
    debugging++;
    if (p) {
		unsigned n = TYPEID (p);
		switch (n) {
	    case TYPEID_ptr : {
			DEBUG_c_class (p->ag_ptr, indent);
			break;
	    }
	    case TYPEID_list :
	    case TYPEID_stack : {
			print_indent (indent, "{\n", f);
			while (!IS_NULL_list (p)) {
				c_class *q = (HEAD_list (p))->ag_ptr;
				DEBUG_c_class (q, indent + 1);
				p = TAIL_list (p);
			}
			print_indent (indent, "}\n", f);
			break;
	    }
	    case TYPEID_exp : {
			print_stmt (p, indent, 1, f);
			break;
	    }
	    default : {
			print_indent (indent, "", f);
			switch (n) {
		    case TYPEID_ctype : DEBUG_ctype (p); break;
		    case TYPEID_err : report (crt_loc, p); break;
		    case TYPEID_etype : DEBUG_etype (p); break;
		    case TYPEID_flt : DEBUG_flt (p); break;
		    case TYPEID_ftype : DEBUG_ftype (p); break;
		    case TYPEID_graph : DEBUG_graph (p); break;
		    case TYPEID_hashid : DEBUG_hashid (p); break;
		    case TYPEID_id : DEBUG_id_long (p); break;
		    case TYPEID_inst : DEBUG_inst (p); break;
		    case TYPEID_itype : DEBUG_itype (p); break;
		    case TYPEID_member : DEBUG_member (p); break;
		    case TYPEID_nat : DEBUG_nat (p); break;
		    case TYPEID_nspace : DEBUG_nspace (p); break;
		    case TYPEID_off : DEBUG_offset (p); break;
		    case TYPEID_str : DEBUG_str (p); break;
		    case TYPEID_tok : DEBUG_sort (p); break;
		    case TYPEID_type : DEBUG_type (p); break;
		    case TYPEID_virt : DEBUG_virt (p); break;
		    case TYPEID_free : fputs_v ("FREE\n", f); break;
		    default : fputs_v ("UNKNOWN\n", f); break;
			}
			break;
	    }
		}
    } else {
		print_indent (indent, "NULL\n", f);
    }
    fflush_v (f);
    debugging--;
    return;
}

void
debug(c_class *p)
{
    DEBUG_c_class (p, 0);
    return;
}

#ifdef DEBUG
#undef DEBUG
#endif

void
DEBUG(c_class *p)
{
    DEBUG_c_class (p, 0);
    return;
}

#endif /* c_class_IMPLEMENTATION */


/*
 *    PARSER TERMINALS
 *
 *    The terminals used in the parser are listed in two places - in symbols.h
 *    and in syntax.sid.
 */

#define LEX_TOKEN(A, B, C)	print_terminal ((A), #A, m);


/*
 *    TERMINAL COUNT
 *
 *    This variable is used to keep count of the number of lexical tokens
 *    printed.
 */

static int terminal_no = 0;


/*
 *    PRINT A TERMINAL
 *
 *    This routine prints the single terminal, term, with the given return
 *    type.  The use argument may be set to false to indicate that the
 *    terminal is not used in the sid parser.
 */

static void
print_terminal(int t, char *term, int m)
{
    char c;
    FILE *f = DEBUG_file;
    unsigned long col = 0;
    unsigned long tab = tab_width;
    while (*term == ' ') term++;
    if (t != terminal_no) {
		error (ERROR_WARNING, "Value of '%s' wrong", term);
    }
    if (m) {
		term += strlen ("lex_");
    } else {
		fprintf_v (f, "#define ");
		col = (unsigned long) strlen ("#define ");
    }
    while (c = *(term++), (c != 0 && c != ' ')) {
		if (c == '_' && m) c = '-';
		fputc_v (c, f);
		col++;
    }
    if (m) {
		fprintf_v (f, " ;\n");
    } else {
		while (col < 5 * tab) {
			fputc_v ('\t', f);
			col = tab * (col / tab + 1);
		}
		fprintf_v (f, "%d\n", terminal_no);
    }
    terminal_no++;
    return;
}


/*
 *    PRINT ALL THE TERMINALS
 *
 *    This routine prints all the terminals in a form acceptable to sid.
 */

static void
sid_terminals(int m)
{
    FILE *f = DEBUG_file;
    terminal_no = 0;
    fprintf_v (f, "/* Automatically generated list of terminals */\n");
#include "symbols.h"
#undef LEX_TOKEN
    return;
}


/*
 *    PRINT ALL THE ERRORS
 *
 *    This routine lists all the error names.
 */

static void
list_errors()
{
    FILE *f = DEBUG_file;
    ERR_DATA *p = ERR_CATALOG;
    init_option (0);
    while (p->name) {
		fprintf_v (f, "%s\n", p->name);
		p++;
    }
    return;
}


/*
 *    PRINT ALL THE OPTIONS
 *
 *    This routine lists all the option names.
 */

static void
list_options()
{
    FILE *f = DEBUG_file;
    OPT_DATA *p = OPT_CATALOG;
    while (p->name) {
		fprintf_v (f, "%s\n", p->name);
		p++;
    }
    return;
}


/*
 *    DEFINE ALL THE OPTION VALUES
 *
 *    This routine prints a list of all option values in a form suitable
 *    as a usage list in the error catalogue.
 */

static void
define_options()
{
    int col = 0;
    int comma = 0;
    const char *s;
    FILE *f = DEBUG_file;
    OPT_DATA *p = OPT_CATALOG;
    fprintf_v (f, "    ");
    while (s = p->name, s != NULL) {
		char c;
		if (comma) {
			fputc_v (',', f);
			col++;
		}
		if (col > 60) {
			fprintf_v (f, "\n    ");
			comma = 0;
			col = 0;
		}
		if (comma) {
			fputc_v (' ', f);
			col++;
		}
		while (c = *(s++), c != 0) {
			if (c == '.') c = '_';
			fputc_v (c, f);
			col++;
		}
		comma = 1;
		p++;
    }
    fputc_v ('\n', f);
    return;
}


/*
 *    HANDLE DEBUGGING OPTIONS
 *
 *    This routine is called to handle the debug option '-d arg'.
 */

void
debug_option(char *arg)
{
    if (streq (arg, "error")) {
		list_errors ();
    } else if (streq (arg, "lex")) {
		sid_terminals (0);
    } else if (streq (arg, "opt")) {
		define_options ();
    } else if (streq (arg, "option")) {
		list_options ();
    } else if (streq (arg, "sid")) {
		sid_terminals (1);
    } else {
		error (ERROR_WARNING, "Unknown option, '-d%s'", arg);
    }
    return;
}


#else /* RUNTIME */


/*
 *    DUMMY DEBUGGING ROUTINE
 *
 *    This routine is a dummy which is used when run-time routines are
 *    not enabled.
 */

#if c_class_IMPLEMENTATION

void
debug(c_class *p)
{
    error (ERROR_INTERNAL, "Not compiled with debugging enabled");
    UNUSED (p);
    return;
}

#endif /* c_class_IMPLEMENTATION */


#endif /* RUNTIME */
