/*
 * 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/print.c,v 1.10 2004/08/15 11:13:37 bp Exp $
 */


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

#include <ctype.h>

#include "msgcat.h"

#include "system.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "etype_ops.h"
#include "exp_ops.h"
#include "flt_ops.h"
#include "ftype_ops.h"
#include "graph_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "itype_ops.h"
#include "member_ops.h"
#include "nat_ops.h"
#include "nspace_ops.h"
#include "off_ops.h"
#include "str_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "basetype.h"
#include "buffer.h"
#include "char.h"
#include "class.h"
#include "constant.h"
#include "convert.h"
#include "debug.h"
#include "exception.h"
#include "file.h"
#include "function.h"
#include "hash.h"
#include "label.h"
#include "lex.h"
#include "literal.h"
#include "print.h"
#include "redeclare.h"
#include "symbols.h"
#include "syntax.h"
#include "tok.h"
#include "token.h"
#include "ustring.h"


/*
 *    PRINT BUFFER
 *
 *    This buffer is used for the error output.
 */

BUFFER print_buff = NULL_buff;


/*
 *    FUNCTION DECLARATIONS
 *
 *    A couple of forward function declarations are necessary.
 */

static int print_head(TYPE, int, BUFFER *, int);
static int print_tail(TYPE, BUFFER *, int);


/*
 *    PRINT FLAGS
 *
 *    These flags are used to control the form of the output.
 */

static int print_default_args = 0;
static int print_member_type = 0;
static int print_return_type = 1;
static int print_func_params = 0;
static int print_except = 0;
static int print_func_linkage = 0;
static int print_parent_namespace = 1;
static int print_bitfield_sep = ':';
int print_c_style = LANGUAGE_C;
int print_type_alias = 0;
int print_uniq_anon = 0;
int print_id_desc = 0;


/*
 *    PRINT A LEXICAL TOKEN NAME
 *
 *    This routine prints the name of the lexical token t to the buffer bf.
 *    With all the routines in this file the sp argument is true if a space
 *    should be printed before the actual text, and the return value is true
 *    if a space should be printed after it.
 */

int
print_lex(int t, BUFFER *bf, int sp)
{
    string s = token_name (t);
    if (s == NULL) return (sp);
    if (sp) bfputc (bf, ' ');
    bfputs (bf, s);
    return (1);
}


/*
 *    PRINT AN ACCESS SPECIFIER
 *
 *    This routine prints the access specifier n to the buffer bf.
 */

int
print_access(DECL_SPEC n, BUFFER *bf, int sp)
{
    int t;
    DECL_SPEC a = (n & dspec_access);
    if (a == dspec_private) {
		t = lex_private;
    } else if (a == dspec_protected) {
		t = lex_protected;
    } else {
		t = lex_public;
    }
    sp = print_lex (t, bf, sp);
    return (sp);
}


/*
 *    BUILT-IN TYPE NAMES
 *
 *    This table gives the names of the built-in types.
 */

const char *ntype_name [ ORDER_ntype ] = {
    "<error>",			/* ntype_none */
    "char",			/* ntype_char */
    "signed char",		/* ntype_schar */
    "unsigned char",		/* ntype_uchar */
    "short",			/* ntype_sshort */
    "unsigned short",		/* ntype_ushort */
    "int",			/* ntype_sint */
    "unsigned int",		/* ntype_uint */
    "long",			/* ntype_slong */
    "unsigned long",		/* ntype_ulong */
    "long long",		/* ntype_sllong */
    "unsigned long long",	/* ntype_ullong */
    "float",			/* ntype_float */
    "double",			/* ntype_double */
    "long double",		/* ntype_ldouble */
    "void",			/* ntype_void */
    "<bottom>",			/* ntype_bottom */
#if LANGUAGE_CPP
    "bool",			/* ntype_bool */
#else
    "_Bool",			/* ntype_bool */
#endif
    "ptrdiff_t",		/* ntype_ptrdiff_t */
    "size_t",			/* ntype_size_t */
    "wchar_t",			/* ntype_wchar_t */
    "..."			/* ntype_ellipsis */
};


/*
 *    PRINT A BUILT-IN TYPE
 *
 *    This routine prints the built-in type n to the buffer bf.
 */

int
print_ntype(BUILTIN_TYPE n, BUFFER *bf, int sp)
{
    if (sp) bfputc (bf, ' ');
    bfputs (bf, ustrlit (ntype_name [n]));
    return (1);
}


/*
 *    PRINT A BASE TYPE
 *
 *    This routine prints the base type n to the buffer bf.
 */

int
print_btype(BASE_TYPE n, BUFFER *bf, int sp)
{
    BASE_TYPE key = (n & btype_named);
    if (key) {
		switch (key) {
	    case btype_class : sp = print_lex (lex_class, bf, sp); break;
	    case btype_struct : sp = print_lex (lex_struct, bf, sp); break;
	    case btype_union : sp = print_lex (lex_union, bf, sp); break;
	    case btype_enum : sp = print_lex (lex_enum, bf, sp); break;
	    case btype_any : sp = print_lex (lex_tag_Hcap, bf, sp); break;
		}
    } else {
		if (n & btype_signed) sp = print_lex (lex_signed, bf, sp);
		if (n & btype_unsigned) sp = print_lex (lex_unsigned, bf, sp);
		if (n & btype_short) sp = print_lex (lex_short, bf, sp);
		if (n & btype_long) sp = print_lex (lex_long, bf, sp);
		if (n & btype_long2) sp = print_lex (lex_long, bf, sp);
		if (n & btype_char) sp = print_lex (lex_char, bf, sp);
		if (n & btype_int) sp = print_lex (lex_int, bf, sp);
		if (n & btype_float) sp = print_lex (lex_float, bf, sp);
		if (n & btype_double) sp = print_lex (lex_double, bf, sp);
		if (n & btype_void) sp = print_lex (lex_void, bf, sp);
		if (n & btype_bottom) sp = print_lex (lex_bottom, bf, sp);
		if (n & btype_bool) sp = print_lex (lex_bool, bf, sp);
		if (n & btype_ptrdiff_t) sp = print_lex (lex_ptrdiff_Ht, bf, sp);
		if (n & btype_size_t) sp = print_lex (lex_size_Ht, bf, sp);
		if (n & btype_wchar_t) sp = print_lex (lex_wchar_Ht, bf, sp);
		if (n & btype_ellipsis) sp = print_lex (lex_ellipsis, bf, sp);
		if (n & btype_star) sp = print_lex (lex_star, bf, sp);
    }
    return (sp);
}


/*
 *    PRINT A CONST-VOLATILE QUALIFIER
 *
 *    This routine prints the const-volatile qualifier n to the buffer bf.
 */

int
print_cv(CV_SPEC n, BUFFER *bf, int sp)
{
    if (n) {
		if (n & cv_const) sp = print_lex (lex_const, bf, sp);
		if (n & cv_restrict) sp = print_lex (lex_restrict, bf, sp);
		if (n & cv_volatile) sp = print_lex (lex_volatile, bf, sp);
    }
    return (sp);
}


/*
 *    PRINT A DECLARATION SPECIFIER
 *
 *    This routine prints the declaration specifier n to the buffer bf.  The
 *    C and C++ linkage specifiers are not included.
 */

int
print_dspec(DECL_SPEC n, BUFFER *bf, int sp)
{
    if (n & dspec_typedef) sp = print_lex (lex_typedef, bf, sp);
    if (n & dspec_storage) {
		if (n & dspec_extern) sp = print_lex (lex_extern, bf, sp);
		if (n & dspec_static) sp = print_lex (lex_static, bf, sp);
		if (n & dspec_register) sp = print_lex (lex_register, bf, sp);
		if (n & dspec_auto) sp = print_lex (lex_auto, bf, sp);
		if (n & dspec_mutable) sp = print_lex (lex_mutable, bf, sp);
    }
    if (n & dspec_function) {
		if (n & dspec_explicit) sp = print_lex (lex_explicit, bf, sp);
		if (n & dspec_friend) sp = print_lex (lex_friend, bf, sp);
		if (n & dspec_inline) sp = print_lex (lex_inline, bf, sp);
		if (n & dspec_virtual) sp = print_lex (lex_virtual, bf, sp);
    }
    return (sp);
}


/*
 *    PRINT A LINKAGE SPECIFIER
 *
 *    This routine prints the linkage specifier n to the buffer bf.
 */

static int
print_linkage(CV_SPEC n, BUFFER *bf, int sp)
{
    CV_SPEC m = (n & cv_language);
    if (m) {
		if (m != cv_cpp && !print_c_style) {
			string s = linkage_string (dspec_none, m);
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "extern \"%s\"", s);
			sp = 1;
		}
    }
    return (sp);
}


/*
 *    PRINT A BUFFER LOCATION
 *
 *    This routine prints the file location p to the buffer bf.  q gives
 *    the main location.  If the file names from p and q match then only
 *    the line number is printed.
 */

int
print_loc(LOCATION *p, LOCATION *q, BUFFER *bf, int sp)
{
    string fn;
    unsigned long ln;
    if (sp) bfputc (bf, ' ');
    if (p == NULL) {
		fn = ustrlit ("<unknown>");
		ln = 0;
    } else {
		PTR (POSITION) pa = p->posn;
		if (IS_NULL_ptr (pa)) {
			fn = ustrlit ("<unknown>");
		} else {
			fn = DEREF_string (posn_file (pa));
			if (q) {
				/* Check previous location */
				PTR (POSITION) qa = q->posn;
				if (EQ_ptr (qa, pa)) {
					fn = NULL;
				} else if (!IS_NULL_ptr (qa)) {
					string gn = DEREF_string (posn_file (qa));
					if (ustreq (gn, fn)) fn = NULL;
				}
			}
		}
		ln = p->line;
    }
    if (fn) {
		bfprintf (bf, "\"%s\"", fn);
		if (ln) bfprintf (bf, ", line %lu", ln);
    } else {
		bfprintf (bf, "line %lu", ln);
    }
    return (1);
}


/*
 *    PRINT A HASH TABLE ENTRY
 *
 *    This routine prints the identifier name corresponding to the hash
 *    table entry p to the buffer bf.
 */

int
print_hashid(HASHID p, int sep, int anon, BUFFER *bf, int sp)
{
    unsigned tag;
    if (IS_NULL_hashid (p)) return (sp);
    tag = TAG_hashid (p);
    ASSERT (ORDER_hashid == 7);
    switch (tag) {
	case hashid_name_tag :
	case hashid_ename_tag : {
	    /* Simple name */
	    string s = DEREF_string (hashid_name_etc_text (p));
	    if (sp) bfputc (bf, ' ');
	    bfputs (bf, s);
	    sp = 1;
	    break;
	}
	case hashid_constr_tag :
	case hashid_destr_tag : {
	    /* Class destructor name */
	    IDENTIFIER tid = DEREF_id (hashid_constr_etc_tid (p));
	    if (IS_NULL_id (tid)) {
			/* Unnamed constructor or destructor */
			TYPE t = DEREF_type (hashid_constr_etc_type (p));
			if (sep) {
				IGNORE print_type (t, bf, sp);
				bfprintf (bf, "::");
			} else {
				if (sp) bfputc (bf, ' ');
			}
			if (tag == hashid_destr_tag) bfputc (bf, '~');
			sp = print_type (t, bf, 0);
	    } else {
			/* Named constructor or destructor */
			static HASHID lastp = NULL_hashid;
			if (sep && !EQ_hashid (p, lastp)) {
				lastp = p;
				tid = DEREF_id (hashid_id (p));
				sp = print_id_short (tid, qual_none, bf, sp);
				lastp = NULL_hashid;
				break;
			}
			p = DEREF_hashid (id_name (tid));
			if (sp) bfputc (bf, ' ');
			if (sep) {
				IGNORE print_hashid (p, 0, 1, bf, 0);
				bfprintf (bf, "::");
			}
			if (tag == hashid_destr_tag) bfputc (bf, '~');
			sp = print_hashid (p, 0, 1, bf, 0);
	    }
	    break;
	}
	case hashid_conv_tag : {
	    /* Overloaded conversion name */
	    int prt = print_return_type;
	    TYPE t = DEREF_type (hashid_conv_type (p));
	    sp = print_lex (lex_operator, bf, sp);
	    print_return_type = 1;
	    sp = print_type (t, bf, sp);
	    print_return_type = prt;
	    break;
	}
	case hashid_op_tag : {
	    /* Overloaded operator name */
	    int op = DEREF_int (hashid_op_lex (p));
	    string s = token_name (op);
	    sp = print_lex (lex_operator, bf, sp);
	    if (s) {
			int c = (int) *s;
			if (isalpha (c)) bfputc (bf, ' ');
			bfputs (bf, s);
	    }
	    break;
	}
	case hashid_anon_tag : {
	    /* Anonymous identifier */
	    if (anon) {
			unsigned long u = DEREF_ulong (hashid_anon_uniq (p));
			if (sp) bfputc (bf, ' ');
			if (print_uniq_anon) {
				bfprintf (bf, "<anon%x.%lu>", uniq_string, u);
			} else {
				bfprintf (bf, "<anon%lu>", u);
			}
			sp = 1;
	    }
	    break;
	}
    }
    return (sp);
}


/*
 *    PRINT AN IDENTIFIER (SHORT FORM)
 *
 *    This routines prints the short form of the identifier id to the buffer bf.
 */

int
print_id_short(IDENTIFIER id, QUALIFIER qual, BUFFER *bf, int sp)
{
    if (!IS_NULL_id (id)) {
		int sep = 0;
		HASHID p = DEREF_hashid (id_name (id));
		
		/* Print enclosing namespace name */
		NAMESPACE ns = DEREF_nspace (id_parent (id));
		if (sp) bfputc (bf, ' ');
		if (print_parent_namespace && !print_c_style) {
			if (IS_NULL_nspace (ns)) {
				if (qual == qual_full || qual == qual_top) {
					bfprintf (bf, "::");
				}
				sep = 1;
			} else {
				IGNORE print_nspace (ns, qual, 1, bf, 0);
			}
		}
		
		/* Print identifier name */
		if (IS_hashid_anon (p)) {
			/* Print anonymous identifier names */
			IDENTIFIER alt = id;
			unsigned tag = TAG_id (id);
			if (tag == id_token_tag) {
				DECL_SPEC ds = DEREF_dspec (id_storage (id));
				if (ds & dspec_auto) {
					alt = DEREF_id (id_token_alt (id));
					tag = id_parameter_tag;
				}
			}
			if (tag == id_parameter_tag) {
				/* Parameter names */
				unsigned long n = 0;
				MEMBER mem = DEREF_member (nspace_last (ns));
				id = DEREF_id (id_alias (id));
				alt = DEREF_id (id_alias (alt));
				while (!IS_NULL_member (mem)) {
					IDENTIFIER mid = DEREF_id (member_id (mem));
					if (!IS_NULL_id (mid)) {
						if (EQ_id (mid, id)) n = 0;
						if (EQ_id (mid, alt)) n = 0;
						n++;
					}
					mem = DEREF_member (member_next (mem));
				}
				bfprintf (bf, "<param%lu>", n);
			} else {
				/* Other identifiers */
				LOCATION loc;
				string fn, fs;
				DEREF_loc (id_loc (id), loc);
				fs = DEREF_string (posn_file (loc.posn));
				fn = ustrrchr (fs, '/');
				fn = (fn ? fn + 1 : fs);
				bfprintf (bf, "(\"%s\":%lu)", fn, loc.line);
			}
		} else {
			/* Print other identifier names */
			IGNORE print_hashid (p, sep, 0, bf, 0);
		}
		sp = 1;
    }
    return (sp);
}


/*
 *    PRINT AN IDENTIFIER (LONG FORM)
 *
 *    This routines prints the long form of the identifier id to the buffer bf.
 */

int
print_id_long(IDENTIFIER id, QUALIFIER qual, BUFFER *bf, int sp)
{
    if (!IS_NULL_id (id)) {
		int prt = 1;
		int key = 0;
		TYPE t = NULL_type;
		TYPE f = NULL_type;
		TOKEN tok = NULL_tok;
		GRAPH gr = NULL_graph;
		const char *desc = NULL;
		int full = print_id_desc;
		NAMESPACE pns = NULL_nspace;
		LIST (TYPE) p = NULL_list (TYPE);
		switch (TAG_id (id)) {
	    case id_keyword_tag :
	    case id_iso_keyword_tag : {
			/* Keywords */
			if (full) desc = "keyword";
			break;
	    }
	    case id_builtin_tag : {
			/* Built-in operators */
			t = DEREF_type (id_builtin_ret (id));
			p = DEREF_list (id_builtin_ptypes (id));
			if (full) desc = "built-in";
			break;
	    }
	    case id_obj_macro_tag :
	    case id_func_macro_tag : {
			/* Macros */
			if (full) desc = "macro";
			break;
	    }
	    case id_predicate_tag : {
			/* Predicates */
			if (full) desc = "predicate";
			break;
	    }
	    case id_class_name_tag :
	    case id_enum_name_tag : {
			/* Class and enumeration names */
			f = DEREF_type (id_class_name_etc_defn (id));
			key = (full || print_c_style);
			break;
	    }
	    case id_class_alias_tag :
	    case id_enum_alias_tag :
	    case id_type_alias_tag : {
			/* Typedef names */
			if (full) {
				t = DEREF_type (id_class_name_etc_defn (id));
				desc = "typedef";
			}
			break;
	    }
	    case id_nspace_name_tag : {
			/* Namespace names */
			if (full) desc = "namespace";
			break;
	    }
	    case id_nspace_alias_tag : {
			/* Namespace aliases */
			if (full) {
				desc = "namespace";
				pns = DEREF_nspace (id_nspace_alias_defn (id));
			}
			break;
	    }
	    case id_variable_tag :
	    case id_parameter_tag : {
			/* Object names */
			if (full) {
				DECL_SPEC ds = DEREF_dspec (id_storage (id));
				if (ds & dspec_auto) desc = "auto";
				t = DEREF_type (id_variable_etc_type (id));
			}
			break;
	    }
	    case id_stat_member_tag : {
			/* Static members */
			if (full) {
				t = DEREF_type (id_stat_member_type (id));
				desc = "static";
			}
			break;
	    }
	    case id_weak_param_tag : {
			if (full) {
				desc = "auto";
				t = type_sint;
			}
			break;
	    }
	    case id_function_tag :
	    case id_mem_func_tag : {
			/* Function names */
			HASHID nm = DEREF_hashid (id_name (id));
			switch (TAG_hashid (nm)) {
		    case hashid_constr_tag :
		    case hashid_destr_tag :
		    case hashid_conv_tag : {
				/* Inhibit return type */
				prt = 0;
				break;
		    }
			}
			t = DEREF_type (id_function_etc_type (id));
			f = DEREF_type (id_function_etc_form (id));
			if (!IS_NULL_type (f) && !IS_type_token (f)) {
				f = NULL_type;
			}
			if (full) {
				DECL_SPEC ds = DEREF_dspec (id_storage (id));
				if (ds & dspec_implicit) desc = "implicit";
			} else {
				if (print_c_style) t = NULL_type;
			}
			break;
	    }
	    case id_stat_mem_func_tag : {
			/* Static function member names */
			t = DEREF_type (id_stat_mem_func_type (id));
			f = DEREF_type (id_stat_mem_func_form (id));
			if (!IS_NULL_type (f) && !IS_type_token (f)) {
				f = NULL_type;
			}
			if (full) {
				desc = "static";
			} else {
				if (print_c_style) t = NULL_type;
			}
			break;
	    }
	    case id_member_tag : {
			/* Member names */
			gr = DEREF_graph (id_member_base (id));
			if (full) t = DEREF_type (id_member_type (id));
			break;
	    }
	    case id_enumerator_tag : {
			/* Enumerator names */
			if (full) desc = "enumerator";
			break;
	    }
	    case id_label_tag : {
			/* Label names */
			if (full) {
				int op = DEREF_int (id_label_op (id));
				if (op != lex_identifier) {
					/* Print kind of label */
					sp = print_lex (op, bf, sp);
					if (op == lex_case) {
						NAT n = find_case_nat (id);
						sp = print_nat (n, 0, bf, sp);
					}
					return (sp);
				}
				desc = "label";
			}
			break;
	    }
	    case id_token_tag : {
			/* Token names */
			if (full) {
				tok = DEREF_tok (id_token_sort (id));
				desc = "token";
			}
			break;
	    }
		}
		if (desc) {
			/* Print description */
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, desc);
			sp = 1;
		}
		if (!IS_NULL_tok (tok)) {
			/* Print token sort */
			sp = print_sort (tok, 0, bf, sp);
		}
		if (!IS_NULL_type (t)) {
			/* Print start of identifier type */
			print_return_type = prt;
			sp = print_head (t, key, bf, sp);
			print_return_type = 1;
		}
		if (IS_NULL_type (f)) {
			if (!IS_NULL_graph (gr)) {
				sp = print_graph (gr, 1, bf, sp);
				print_parent_namespace = 0;
			}
			sp = print_id_short (id, qual, bf, sp);
			print_parent_namespace = 1;
		} else {
			sp = print_head (f, key, bf, sp);
			sp = print_tail (f, bf, sp);
		}
		if (!IS_NULL_type (t)) {
			/* Print end of identifier type */
			print_default_args = 1;
			sp = print_tail (t, bf, sp);
			print_default_args = 0;
		}
		if (!IS_NULL_list (p)) {
			/* Print parameter types */
			sp = print_type_list (p, bf, 1);
		}
		if (!IS_NULL_nspace (pns)) {
			/* Print namespace alias definition */
			sp = print_lex (lex_assign, bf, sp);
			sp = print_nspace (pns, qual_none, 0, bf, sp);
		}
    }
    return (sp);
}


/*
 *    PRINT A NAMESPACE
 *
 *    This routines prints the namespace ns to the buffer bf.  The argument
 *    pre is true if this a prefix to an identifier and false if the namespace
 *    itself is what is required.
 */

int
print_nspace(NAMESPACE ns, QUALIFIER qual, int pre, BUFFER *bf, int sp)
{
    if (!IS_NULL_nspace (ns)) {
		IDENTIFIER id = DEREF_id (nspace_name (ns));
		switch (TAG_nspace (ns)) {
	    case nspace_named_tag :
	    case nspace_ctype_tag : {
			/* Named and class namespaces */
			if (sp) bfputc (bf, ' ');
			if (IS_id_class_name (id)) {
				TYPE t = DEREF_type (id_class_name_defn (id));
				if (IS_type_compound (t)) {
					int key = 0;
					CLASS_TYPE ct;
					if (!pre) key = print_c_style;
					ct = DEREF_ctype (type_compound_defn (t));
					IGNORE print_ctype (ct, qual, key, bf, 0);
				} else {
					IGNORE print_id_short (id, qual, bf, 0);
				}
			} else {
				IGNORE print_id_short (id, qual, bf, 0);
			}
			if (pre) bfprintf (bf, "::");
			sp = 1;
			break;
	    }
	    case nspace_unnamed_tag : {
			/* Unnamed namespaces */
			if (sp) bfputc (bf, ' ');
			ns = DEREF_nspace (id_parent (id));
			IGNORE print_nspace (ns, qual, 1, bf, 0);
			if (print_uniq_anon) {
				bfprintf (bf, "<anon%x>", uniq_string);
			} else {
				bfprintf (bf, "<anon>");
			}
			if (pre) bfprintf (bf, "::");
			sp = 1;
			break;
	    }
	    case nspace_global_tag : {
			/* The global namespace */
			if (!pre || qual == qual_full || qual == qual_top) {
				if (sp) bfputc (bf, ' ');
				bfprintf (bf, "::");
				sp = 1;
			}
			break;
	    }
	    default : {
			/* Other namespaces */
			if (!pre) sp = print_id_short (id, qual, bf, sp);
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    PRINT A GRAPH
 *
 *    This routines prints the graph gr to the buffer bf.  The argument sep is
 *    true to indicate that a terminal '::' should be included.
 */

int
print_graph(GRAPH gr, int sep, BUFFER *bf, int sp)
{
    if (!IS_NULL_graph (gr)) {
		CLASS_TYPE ct = DEREF_ctype (graph_head (gr));
		GRAPH gs = DEREF_graph (graph_up (gr));
		if (sp) {
			bfputc (bf, ' ');
			sp = 0;
		}
		if (!IS_NULL_graph (gs)) {
			IGNORE print_graph (gs, 1, bf, 0);
			print_parent_namespace = 0;
		}
		IGNORE print_ctype (ct, qual_none, 0, bf, 0);
		print_parent_namespace = 1;
		if (sep) bfprintf (bf, "::");
    }
    return (sp);
}


/*
 *    PRINT A PREPROCESSING TOKEN
 *
 *    This routine prints the preprocessing token p to the buffer bf.  It
 *    is used not only in the error reporting routines, but also in the
 *    preprocessing action.
 */

int
print_pptok(PPTOKEN *p, BUFFER *bf, int sp)
{
    int q;
    int t = p->tok;
    if (sp) bfputc (bf, ' ');
    switch (t) {
	case lex_identifier :
	case lex_type_Hname :
	case lex_namespace_Hname :
	case lex_statement_Hname :
		identifier_label : {
			/* Identifiers */
			HASHID nm = p->pp_data.id.hash;
			string s = DEREF_string (hashid_name_etc_text (nm));
			bfputs (bf, s);
			break;
		}
	case lex_destructor_Hname : {
	    /* Destructor names */
	    bfputc (bf, '~');
	    goto identifier_label;
	}
	case lex_template_Hid :
	case lex_template_Htype : {
	    /* Template names */
	    IDENTIFIER id = p->pp_data.tok.id;
	    IGNORE print_id_short (id, qual_none, bf, 0);
	    break;
	}
	case lex_integer_Hlit : {
	    /* Integer and floating point literals */
	    string s = p->pp_data.text;
	    bfputs (bf, s);
	    break;
	}
	case lex_char_Hlit : {
	    /* Character literals */
	    q = '\'';
	    string_label : {
			string s = p->pp_data.str.start;
			string e = p->pp_data.str.end;
			bfputc (bf, q);
			while (s != e) {
				bfputc (bf, (int) *s);
				s++;
			}
			bfputc (bf, q);
	    }
	    break;
	}
	case lex_wchar_Hlit : {
	    /* Wide character literals */
	    bfputc (bf, 'L');
	    q = '\'';
	    goto string_label;
	}
	case lex_string_Hlit : {
	    /* String literals */
	    q = '"';
	    goto string_label;
	}
	case lex_wstring_Hlit : {
	    /* Wide string literals */
	    bfputc (bf, 'L');
	    q = '"';
	    goto string_label;
	}
	case lex_integer_Hexp :
	case lex_floating_Hexp :
	case lex_char_Hexp :
	case lex_wchar_Hexp :
	case lex_string_Hexp :
	case lex_wstring_Hexp : {
	    /* Literal expressions */
	    IGNORE print_exp (p->pp_data.exp, 0, bf, 0);
	    break;
	}
	case lex_unknown : {
	    /* Unknown characters */
	    unsigned long u;
	    int ch = CHAR_SIMPLE;
	    u = get_multi_char (p->pp_data.buff, &ch);
	    if (ch == CHAR_SIMPLE) {
			bfputc (bf, (int) u);
	    } else {
			print_char (u, ch, 0, bf);
	    }
	    break;
	}
	case lex_nested_Hname : {
	    /* Nested name qualifier */
	    NAMESPACE ns = p->pp_data.ns;
	    IGNORE print_nspace (ns, qual_nested, 1, bf, 0);
	    break;
	}
	case lex_full_Hname : {
	    /* Nested name qualifier */
	    NAMESPACE ns = p->pp_data.ns;
	    IGNORE print_nspace (ns, qual_full, 1, bf, 0);
	    break;
	}
	case lex_nested_Hname_Hstar : {
	    /* Nested member qualifier */
	    IDENTIFIER id = p->pp_data.id.use;
	    IGNORE print_id_short (id, qual_nested, bf, 0);
	    bfprintf (bf, "::*");
	    break;
	}
	case lex_full_Hname_Hstar : {
	    /* Nested member qualifier */
	    IDENTIFIER id = p->pp_data.id.use;
	    IGNORE print_id_short (id, qual_full, bf, 0);
	    bfprintf (bf, "::*");
	    break;
	}
	case lex_complex_Hexp :
	case lex_complex_Htype : {
	    /* Token applications etc. */
	    IDENTIFIER id = p->pp_data.tok.id;
	    IGNORE print_id_short (id, qual_none, bf, 0);
	    break;
	}
	case lex_macro_Harg : {
	    /* Macro parameters */
	    HASHID nm = p->pp_data.par.hash;
	    string s = DEREF_string (hashid_name_etc_text (nm));
	    bfputs (bf, s);
	    break;
	}
	default : {
	    /* Simple token */
	    if (t >= 0) {
			string s = token_name (t);
			bfputs (bf, s);
	    } else {
			bfprintf (bf, "<ignore>");
	    }
	    break;
	}
    }
    return (1);
}


/*
 *    INTEGER LITERAL PRINTING
 *
 *    This prints an integer literal to the buffer bf.  The only difficult case
 *    is when a large literal will not fit into an unsigned long.  The digits
 *    need to be printed in reverse order, and it is again quickest to reverse
 *    the order of the list twice.
 */

int
print_nat(NAT n, int paren, BUFFER *bf, int sp)
{
    if (!IS_NULL_nat (n)) {
		ASSERT (ORDER_nat == 5);
		switch (TAG_nat (n)) {
	    case nat_small_tag : {
			/* Small values */
			unsigned v = DEREF_unsigned (nat_small_value (n));
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "%u", v);
			sp = 1;
			break;
	    }
	    case nat_large_tag : {
			/* Large values */
			LIST (unsigned) p;
			p = DEREF_list (nat_large_values (n));
			if (sp) bfputc (bf, ' ');
			if (LENGTH_list (p) <= 2) {
				/* Two digit literals will fit into a unsigned long */
				unsigned long v = get_nat_value (n);
				bfprintf (bf, "%lu", v);
			} else {
				/* Print large literals by scanning through digits */
				char buff [50];
				const char *fmt = "0x%x";
				LIST (unsigned) q;
				p = REVERSE_list (p);
				q = p;
				while (!IS_NULL_list (q)) {
					unsigned v = DEREF_unsigned (HEAD_list (q));
					sprintf_v (buff, fmt, v);
					bfputs (bf, ustrlit (buff));
					fmt = "%04x";
					q = TAIL_list (q);
				}
				IGNORE REVERSE_list (p);
			}
			sp = 1;
			break;
	    }
	    case nat_calc_tag : {
			/* Calculated values */
			EXP e = DEREF_exp (nat_calc_value (n));
			while (!IS_NULL_exp (e) && IS_exp_cast (e)) {
				e = DEREF_exp (exp_cast_arg (e));
			}
			sp = print_exp (e, paren, bf, sp);
			break;
	    }
	    case nat_neg_tag : {
			/* Negative values */
			NAT m = DEREF_nat (nat_neg_arg (n));
			if (sp) bfputc (bf, ' ');
			bfputc (bf, '-');
			IGNORE print_nat (m, 1, bf, 0);
			break;
	    }
	    case nat_token_tag : {
			IDENTIFIER id = DEREF_id (nat_token_tok (n));
			LIST (TOKEN) args = DEREF_list (nat_token_args (n));
			sp = print_token (id, qual_none, args, bf, sp);
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    FLOATING-POINT LITERAL PRINTING
 *
 *    This routine prints a floating-point literal to the buffer bf.
 */

int
print_flt(FLOAT n, BUFFER *bf, int sp)
{
    if (!IS_NULL_flt (n)) {
		string i = DEREF_string (flt_simple_int_part (n));
		string d = DEREF_string (flt_simple_frac_part (n));
		NAT e = DEREF_nat (flt_simple_exponent (n));
		if (sp) bfputc (bf, ' ');
		bfprintf (bf, "%s.%s", i, d);
		if (!is_zero_nat (e)) {
			bfputc (bf, 'e');
			IGNORE print_nat (e, 0, bf, 0);
		}
		sp = 1;
    }
    return (sp);
}


/*
 *    CHARACTER PRINTING
 *
 *    This routine prints the character c to the buffer bf as a string
 *    character of type ch enclosed by the quote character q.
 */

void
print_char(unsigned long c, int ch, int q, BUFFER *bf)
{
    char buff [20];
    if (ch == CHAR_SIMPLE) {
		switch (c) {
			
	    case char_alert : bfprintf (bf, "\\a"); break;
	    case char_backspace : bfprintf (bf, "\\b"); break;
	    case char_form_feed : bfprintf (bf, "\\f"); break;
	    case char_newline : bfprintf (bf, "\\n"); break;
	    case char_return : bfprintf (bf, "\\r"); break;
	    case char_tab : bfprintf (bf, "\\t"); break;
	    case char_vert_tab : bfprintf (bf, "\\v"); break;
			
	    case char_backslash :
	    case char_question : {
			if (q) bfputc (bf, '\\');
			bfputc (bf, (int) c);
			break;
	    }
			
	    case char_quote :
	    case char_single_quote : {
			int a = (int) c;
			if (a == q) bfputc (bf, '\\');
			bfputc (bf, a);
			break;
	    }
			
	    default : {
			int a = (int) c;
			if (isprint (a)) {
				bfputc (bf, a);
			} else {
				sprintf_v (buff, "\\%03lo", c);
				bfputs (bf, ustrlit (buff));
			}
			break;
	    }
		}
    } else {
		const char *fmt;
		switch (ch) {
	    case CHAR_OCTAL : fmt = "\\%03lo"; break;
	    case CHAR_UNI4 : fmt = "\\u%04lx"; break;
	    case CHAR_UNI8 : fmt = "\\U%08lx"; break;
	    default : fmt = "\\x%lx"; break;
		}
		sprintf_v (buff, fmt, c);
		bfputs (bf, ustrlit (buff));
    }
    return;
}


/*
 *    STRING LITERAL PRINTING
 *
 *    This routine prints a string or character literal to the buffer bf.
 */

int
print_str(STRING s, BUFFER *bf, int sp)
{
    string text;
    int q = '"';
    unsigned kind;
    unsigned long i, len;
	
    if (IS_NULL_str (s)) return (sp);
	
    /* Print the opening quote */
    if (sp) bfputc (bf, ' ');
    kind = DEREF_unsigned (str_simple_kind (s));
    if (kind & STRING_CHAR) q = '\'';
    if (kind & STRING_WIDE) bfputc (bf, 'L');
	
    /* Print the string text */
    text = DEREF_string (str_simple_text (s));
    len = DEREF_ulong (str_simple_len (s));
    bfputc (bf, q);
    if (kind & STRING_MULTI) {
		/* Multi-byte strings */
		for (i = 0; i < len; i++) {
			int ch = CHAR_SIMPLE;
			unsigned long c = get_multi_char (text, &ch);
			print_char (c, ch, q, bf);
			text += MULTI_WIDTH;
		}
    } else {
		/* Simple strings */
		for (i = 0; i < len; i++) {
			unsigned long c = (unsigned long) text [i];
			print_char (c, CHAR_SIMPLE, q, bf);
		}
    }
    bfputc (bf, q);
    return (1);
}


/*
 *    EXPRESSION PRINTING
 *
 *    This routine prints an expression to the buffer bf.
 */

int
print_exp(EXP e, int paren, BUFFER *bf, int sp)
{
    if (!IS_NULL_exp (e)) {
		switch (TAG_exp (e)) {
	    case exp_identifier_tag :
	    case exp_member_tag :
	    case exp_ambiguous_tag :
	    case exp_undeclared_tag : {
			IDENTIFIER id = DEREF_id (exp_identifier_etc_id (e));
			QUALIFIER q = DEREF_qual (exp_identifier_etc_qual (e));
			q &= qual_explicit;
			sp = print_id_short (id, q, bf, sp);
			break;
	    }
	    case exp_int_lit_tag : {
			NAT n = DEREF_nat (exp_int_lit_nat (e));
			sp = print_nat (n, paren, bf, sp);
			break;
	    }
	    case exp_float_lit_tag : {
			FLOAT flt = DEREF_flt (exp_float_lit_flt (e));
			sp = print_flt (flt, bf, sp);
			break;
	    }
	    case exp_char_lit_tag : {
			STRING s = DEREF_str (exp_char_lit_str (e));
			sp = print_str (s, bf, sp);
			break;
	    }
	    case exp_string_lit_tag : {
			STRING s = DEREF_str (exp_string_lit_str (e));
			sp = print_str (s, bf, sp);
			break;
	    }
	    case exp_null_tag :
	    case exp_zero_tag :
	    case exp_value_tag : {
			if (sp) bfputc (bf, ' ');
			bfputc (bf, '0');
			break;
	    }
	    case exp_contents_tag : {
			EXP a = DEREF_exp (exp_contents_ptr (e));
			sp = print_exp (a, 0, bf, sp);
			break;
	    }
	    case exp_token_tag : {
			IDENTIFIER id = DEREF_id (exp_token_tok (e));
			LIST (TOKEN) args = DEREF_list (exp_token_args (e));
			sp = print_token (id, qual_none, args, bf, sp);
			break;
	    }
	    default : {
			static unsigned long exp_no = 0;
#ifdef RUNTIME
			if (debugging) {
				/* Debug expression printing routine */
				sp = print_exp_aux (e, paren, bf, sp);
				break;
			}
#endif
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "<exp%lu>", ++exp_no);
			sp = 1;
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    TOKEN VALUE PRINTING
 *
 *    This routine prints the value of the token tok to the buffer bf.
 */

int
print_tok_value(TOKEN tok, BUFFER *bf, int sp)
{
    if (!IS_NULL_tok (tok)) {
		ASSERT (ORDER_tok == 10);
		switch (TAG_tok (tok)) {
	    case tok_exp_tag : {
			EXP e = DEREF_exp (tok_exp_value (tok));
			sp = print_exp (e, 0, bf, sp);
			break;
	    }
	    case tok_stmt_tag : {
			EXP e = DEREF_exp (tok_stmt_value (tok));
			sp = print_exp (e, 0, bf, sp);
			break;
	    }
	    case tok_nat_tag :
	    case tok_snat_tag : {
			NAT n = DEREF_nat (tok_nat_etc_value (tok));
			sp = print_nat (n, 0, bf, sp);
			break;
	    }
	    case tok_type_tag : {
			TYPE t = DEREF_type (tok_type_value (tok));
			sp = print_type (t, bf, sp);
			break;
	    }
	    case tok_member_tag : {
			OFFSET off = DEREF_off (tok_member_value (tok));
			sp = print_offset (off, bf, sp);
			break;
	    }
	    case tok_class_tag : {
			IDENTIFIER cid = DEREF_id (tok_class_value (tok));
			sp = print_id_short (cid, qual_none, bf, sp);
			break;
	    }
	    default : {
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "<arg>");
			sp = 1;
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    TOKEN APPLICATION PRINTING
 *
 *    This routine prints the token application 'id (args)' or the template
 *    application 'id < args >' to the buffer bf.
 */

int
print_token(IDENTIFIER id, QUALIFIER qual, LIST (TOKEN) args, BUFFER *bf,
			int sp)
{
    int open_bracket = 0;
    int close_bracket = 0;
    if (IS_id_token (id)) {
		/* Token application */
		TOKEN tok = DEREF_tok (id_token_sort (id));
		unsigned tag = TAG_tok (tok);
		IDENTIFIER alt = DEREF_id (id_token_alt (id));
		if (!IS_NULL_id (alt)) id = alt;
		if (tag == tok_proc_tag || !IS_NULL_list (args)) {
			open_bracket = '(';
			close_bracket = ')';
		} else if (tag == tok_class_tag) {
			open_bracket = '<';
			close_bracket = '>';
		}
    } else {
		/* Template application */
		open_bracket = '<';
		close_bracket = '>';
    }
    sp = print_id_short (id, qual, bf, sp);
    if (open_bracket) {
		int first = 1;
		bfputc (bf, ' ');
		bfputc (bf, open_bracket);
		while (!IS_NULL_list (args)) {
			TOKEN ptok = DEREF_tok (HEAD_list (args));
			if (!IS_NULL_tok (ptok)) {
				if (!first) bfputc (bf, ',');
				IGNORE print_tok_value (ptok, bf, 1);
				first = 0;
			}
			args = TAIL_list (args);
		}
		if (!first) bfputc (bf, ' ');
		bfputc (bf, close_bracket);
		sp = 1;
    }
    return (sp);
}


/*
 *    TOKEN SORT PRINTING
 *
 *    This routine prints the token sort tok to the buffer bf.
 */

int
print_sort(TOKEN tok, int arg, BUFFER *bf, int sp)
{
    unsigned tag;
    if (IS_NULL_tok (tok)) return (sp);
    tag = TAG_tok (tok);
    ASSERT (ORDER_tok == 10);
    switch (tag) {
		
	case tok_exp_tag : {
	    /* Expression tokens */
	    sp = print_lex (lex_exp_Hcap, bf, sp);
	    if (!arg) {
			TYPE t = DEREF_type (tok_exp_type (tok));
			CV_SPEC cv = DEREF_cv (type_qual (t));
			if (cv & cv_lvalue) {
				IGNORE print_lex (lex_lvalue, bf, sp);
			} else {
				int c = DEREF_int (tok_exp_constant (tok));
				if (c) IGNORE print_lex (lex_const, bf, sp);
			}
			bfprintf (bf, " :");
			IGNORE print_type (t, bf, 1);
			bfprintf (bf, " :");
			sp = 1;
	    }
	    break;
	}
		
	case tok_nat_tag : {
	    /* Integer constant tokens */
	    if (arg) {
			sp = print_lex (lex_exp_Hcap, bf, sp);
	    } else {
			sp = print_lex (lex_nat_Hcap, bf, sp);
	    }
	    break;
	}
		
	case tok_snat_tag : {
	    /* Integer constant tokens */
	    if (arg) {
			sp = print_lex (lex_exp_Hcap, bf, sp);
	    } else {
			sp = print_lex (lex_int_Hcap, bf, sp);
	    }
	    break;
	}
		
	case tok_stmt_tag : {
	    /* Statement tokens */
	    sp = print_lex (lex_stmt_Hcap, bf, sp);
	    break;
	}
		
	case tok_type_tag : {
	    /* Type tokens */
	    if (arg) {
			sp = print_lex (lex_type_Hcap, bf, sp);
	    } else {
			BASE_TYPE bt = DEREF_btype (tok_type_kind (tok));
			int key = type_token_key (bt);
			if (key == lex_signed || key == lex_unsigned) {
				sp = print_lex (lex_variety_Hcap, bf, sp);
			}
			sp = print_lex (key, bf, sp);
	    }
	    break;
	}
		
	case tok_func_tag : {
	    /* Function tokens */
	    if (arg) {
			sp = print_lex (lex_proc_Hcap, bf, sp);
	    } else {
			TYPE t = DEREF_type (tok_func_type (tok));
			IGNORE print_lex (lex_func_Hcap, bf, sp);
			IGNORE print_type (t, bf, 1);
			bfprintf (bf, " :");
			sp = 1;
	    }
	    break;
	}
		
	case tok_member_tag : {
	    /* Member tokens */
	    TYPE s = DEREF_type (tok_member_of (tok));
	    IGNORE print_lex (lex_member_Hcap, bf, sp);
	    if (!arg) {
			TYPE t = DEREF_type (tok_member_type (tok));
			print_bitfield_sep = '%';
			IGNORE print_type (t, bf, 1);
			print_bitfield_sep = ':';
			bfprintf (bf, " :");
	    }
	    IGNORE print_type (s, bf, 1);
	    bfprintf (bf, " :");
	    sp = 1;
	    break;
	}
		
	case tok_class_tag : {
	    /* Template class tokens */
	    TYPE t = DEREF_type (tok_class_type (tok));
	    while (!IS_NULL_type (t) && IS_type_templ (t)) {
			TOKEN sort = DEREF_tok (type_templ_sort (t));
			sp = print_sort (sort, 0, bf, sp);
			t = DEREF_type (type_templ_defn (t));
	    }
	    sp = print_lex (lex_class, bf, sp);
	    break;
	}
		
	case tok_proc_tag : {
	    /* Procedure tokens */
	    TOKEN res;
	    int simple = 0;
	    LIST (IDENTIFIER) p, q;
	    sp = print_lex (lex_proc_Hcap, bf, sp);
	    if (arg) break;
	    res = DEREF_tok (tok_proc_res (tok));
	    p = DEREF_list (tok_proc_pids (tok));
	    q = DEREF_list (tok_proc_bids (tok));
	    if (EQ_list (p, q)) simple = 1;
	    if (simple) {
			bfprintf (bf, " (");
	    } else {
			bfprintf (bf, " {");
	    }
	    sp = 0;
	    while (!IS_NULL_list (q)) {
			IDENTIFIER id = DEREF_id (HEAD_list (q));
			if (!IS_NULL_id (id)) {
				TOKEN par = DEREF_tok (id_token_sort (id));
				if (sp) bfputc (bf, ',');
				IGNORE print_sort (par, 0, bf, 1);
				if (!simple) {
					HASHID nm = DEREF_hashid (id_name (id));
					IGNORE print_hashid (nm, 0, 0, bf, 1);
				}
				sp = 1;
			}
			q = TAIL_list (q);
	    }
	    if (simple) {
			if (sp) bfputc (bf, ' ');
			bfputc (bf, ')');
	    } else {
			bfprintf (bf, " |");
			sp = 0;
			while (!IS_NULL_list (p)) {
				IDENTIFIER id = DEREF_id (HEAD_list (p));
				if (!IS_NULL_id (id)) {
					if (sp) bfputc (bf, ',');
					if (IS_id_token (id)) {
						/* Simple token parameter */
						HASHID nm = DEREF_hashid (id_name (id));
						TOKEN par = DEREF_tok (id_token_sort (id));
						IGNORE print_sort (par, 1, bf, 1);
						IGNORE print_hashid (nm, 0, 0, bf, 1);
					} else {
						/* Complex type parameter */
						TYPE r;
						r = DEREF_type (id_class_name_etc_defn (id));
						IGNORE print_lex (lex_type_Hcap, bf, 1);
						IGNORE print_type (r, bf, 1);
					}
					sp = 1;
				}
				p = TAIL_list (p);
			}
			bfprintf (bf, " }");
	    }
	    sp = print_sort (res, 0, bf, 1);
	    break;
	}
		
	case tok_templ_tag : {
	    /* Template tokens */
	    LIST (TOKEN) q;
	    LIST (IDENTIFIER) p;
	    DECL_SPEC ex = DEREF_dspec (tok_templ_usage (tok));
	    NAMESPACE ns = DEREF_nspace (tok_templ_pars (tok));
	    if (ex & dspec_extern) {
			/* Exported templates */
			sp = print_lex (lex_export, bf, sp);
	    }
	    IGNORE print_lex (lex_template, bf, sp);
	    if (IS_NULL_nspace (ns)) {
			sp = 1;
			break;
	    }
	    p = DEREF_list (tok_templ_pids (tok));
	    q = DEREF_list (tok_templ_dargs (tok));
	    bfprintf (bf, " <");
	    sp = 0;
	    while (!IS_NULL_list (p)) {
			TOKEN val = NULL_tok;
			IDENTIFIER id = DEREF_id (HEAD_list (p));
			HASHID nm = DEREF_hashid (id_name (id));
			if (!IS_NULL_list (q)) {
				val = DEREF_tok (HEAD_list (q));
				q = TAIL_list (q);
			}
			tok = DEREF_tok (id_token_sort (id));
			tag = TAG_tok (tok);
			if (tag == tok_exp_tag) {
				TYPE t = DEREF_type (tok_exp_type (tok));
				int sp2 = print_head (t, 0, bf, 1);
				sp2 = print_hashid (nm, 0, 0, bf, sp2);
				IGNORE print_tail (t, bf, sp2);
			} else if (tag == tok_type_tag) {
				IGNORE print_lex (lex_class, bf, 1);
				IGNORE print_hashid (nm, 0, 0, bf, 1);
			} else {
				IGNORE print_sort (tok, 0, bf, 1);
				IGNORE print_hashid (nm, 0, 0, bf, 1);
			}
			if (!IS_NULL_tok (val)) {
				bfprintf (bf, " =");
				IGNORE print_tok_value (val, bf, 1);
			}
			p = TAIL_list (p);
			if (!IS_NULL_list (p)) bfputc (bf, ',');
			sp = 1;
	    }
	    if (sp) bfputc (bf, ' ');
	    bfputc (bf, '>');
	    sp = 1;
	    break;
	}
    }
    return (sp);
}


/*
 *    INTEGRAL TYPE PRINTING
 *
 *    This routine prints an integral type to the buffer bf.  Note that the
 *    standard full forms, such as 'signed short int', are translated into
 *    shorter forms, such as 'short'.
 */

int
print_itype(INT_TYPE t, BUFFER *bf, int sp)
{
    if (!IS_NULL_itype (t)) {
		ASSERT (ORDER_itype == 6);
		switch (TAG_itype (t)) {
	    case itype_basic_tag : {
			BUILTIN_TYPE n = DEREF_ntype (itype_basic_no (t));
			sp = print_ntype (n, bf, sp);
			break;
	    }
	    case itype_bitfield_tag : {
			BASE_TYPE bt = DEREF_btype (itype_bitfield_rep (t));
			if (bt & btype_named) {
				TYPE s = DEREF_type (itype_bitfield_sub (t));
				sp = print_type (s, bf, sp);
			} else {
				sp = print_btype (bt, bf, sp);
			}
			break;
	    }
	    case itype_promote_tag : {
			INT_TYPE s = DEREF_itype (itype_promote_arg (t));
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "%s (", special_name (TOK_promote));
			IGNORE print_itype (s, bf, 0);
			bfprintf (bf, ")");
			sp = 1;
			break;
	    }
	    case itype_arith_tag : {
			INT_TYPE s1 = DEREF_itype (itype_arith_arg1 (t));
			INT_TYPE s2 = DEREF_itype (itype_arith_arg2 (t));
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "%s (", special_name (TOK_arith_type));
			IGNORE print_itype (s1, bf, 0);
			bfprintf (bf, ", ");
			IGNORE print_itype (s2, bf, 0);
			bfprintf (bf, ")");
			sp = 1;
			break;
	    }
	    case itype_literal_tag : {
			NAT n = DEREF_nat (itype_literal_nat (t));
			int tok = DEREF_int (itype_literal_spec (t));
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "%s (", special_name (tok));
			IGNORE print_nat (n, 0, bf, 0);
			bfprintf (bf, ")");
			sp = 1;
			break;
	    }
	    case itype_token_tag : {
			BUILTIN_TYPE n = DEREF_ntype (itype_unprom (t));
			if (n == ntype_none || n == ntype_ellipsis) {
				IDENTIFIER id;
				LIST (TOKEN) args;
				id = DEREF_id (itype_token_tok (t));
				args = DEREF_list (itype_token_args (t));
				sp = print_token (id, qual_none, args, bf, sp);
			} else {
				if (sp) bfputc (bf, ' ');
				bfprintf (bf, "%s (", special_name (TOK_promote));
				IGNORE print_ntype (n, bf, 0);
				bfprintf (bf, ")");
				sp = 1;
			}
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    FLOATING-POINT TYPE PRINTING
 *
 *    This routine prints a floating-point type to the buffer bf.
 */

int
print_ftype(FLOAT_TYPE t, BUFFER *bf, int sp)
{
    if (!IS_NULL_ftype (t)) {
		ASSERT (ORDER_ftype == 4);
		switch (TAG_ftype (t)) {
	    case ftype_basic_tag : {
			BUILTIN_TYPE n = DEREF_ntype (ftype_basic_no (t));
			sp = print_ntype (n, bf, sp);
			break;
	    }
	    case ftype_arg_promote_tag : {
			FLOAT_TYPE s = DEREF_ftype (ftype_arg_promote_arg (t));
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "%s (", special_name (TOK_promote));
			IGNORE print_ftype (s, bf, 0);
			bfprintf (bf, ")");
			sp = 1;
			break;
	    }
	    case ftype_arith_tag : {
			FLOAT_TYPE s1 = DEREF_ftype (ftype_arith_arg1 (t));
			FLOAT_TYPE s2 = DEREF_ftype (ftype_arith_arg2 (t));
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "%s (", special_name (TOK_arith_type));
			IGNORE print_ftype (s1, bf, 0);
			bfprintf (bf, ", ");
			IGNORE print_ftype (s2, bf, 0);
			bfprintf (bf, ")");
			sp = 1;
			break;
	    }
	    case ftype_token_tag : {
			IDENTIFIER id = DEREF_id (ftype_token_tok (t));
			LIST (TOKEN) args = DEREF_list (ftype_token_args (t));
			sp = print_token (id, qual_none, args, bf, sp);
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    CLASS TYPE PRINTING
 *
 *    This routines prints the class type ct to the buffer bf.
 */

int
print_ctype(CLASS_TYPE ct, QUALIFIER qual, int key, BUFFER *bf, int sp)
{
    if (!IS_NULL_ctype (ct)) {
		TYPE t = DEREF_type (ctype_form (ct));
		IDENTIFIER id = DEREF_id (ctype_name (ct));
		if (key && IS_id_class_name (id)) {
			BASE_TYPE bt = find_class_key (ct);
			sp = print_btype (bt, bf, sp);
		}
		if (!IS_NULL_type (t) && IS_type_token (t)) {
			IDENTIFIER tid = DEREF_id (type_token_tok (t));
			LIST (TOKEN) args = DEREF_list (type_token_args (t));
			sp = print_token (tid, qual, args, bf, sp);
		} else {
			sp = print_id_short (id, qual, bf, sp);
		}
    }
    return (sp);
}


/*
 *    ENUMERATION TYPE PRINTING
 *
 *    This routines prints the enumeration type et to the buffer bf.
 */

int
print_etype(ENUM_TYPE et, int key, BUFFER *bf, int sp)
{
    if (!IS_NULL_etype (et)) {
		IDENTIFIER id = DEREF_id (etype_name (et));
		if (key && IS_id_enum_name (id)) {
			sp = print_lex (lex_enum, bf, sp);
		}
		sp = print_id_short (id, qual_none, bf, sp);
    }
    return (sp);
}


/*
 *    CHECK FOR TAILED TYPES
 *
 *    This routine tested whether the type t is a tailed type, that is an
 *    array, bitfield, or function type.
 */

static int
is_tailed_type(TYPE t)
{
    if (!IS_NULL_type (t)) {
		IDENTIFIER tid = DEREF_id (type_name (t));
		if (IS_NULL_id (tid) || !print_type_alias) {
			switch (TAG_type (t)) {
			case type_func_tag :
			case type_array_tag :
			case type_bitfield_tag : {
				return (1);
			}
			case type_templ_tag : {
				TYPE s = DEREF_type (type_templ_defn (t));
				return (is_tailed_type (s));
			}
			}
		}
    }
    return (0);
}


/*
 *    TYPE HEAD PRINTING
 *
 *    This routine prints the head of a type (i.e. the main part and any
 *    pointer or reference components) to the buffer bf.
 */

static int
print_head(TYPE t, int key, BUFFER *bf, int sp)
{
    if (IS_NULL_type (t)) {
		static unsigned long type_no = 0;
		if (sp) bfputc (bf, ' ');
		bfprintf (bf, "<type%lu>", ++type_no);
		sp = 1;
    } else {
		CV_SPEC qual = DEREF_cv (type_qual (t));
		IDENTIFIER tid = DEREF_id (type_name (t));
		qual &= cv_qual;
		if (!IS_NULL_id (tid) && print_type_alias) {
			switch (TAG_id (tid)) {
			case id_class_alias_tag :
			case id_enum_alias_tag :
			case id_type_alias_tag : {
				/* Print type aliases */
				if (!IS_type_pre (t)) {
					sp = print_cv (qual, bf, sp);
					sp = print_id_short (tid, qual_none, bf, sp);
					return (sp);
				}
				break;
			}
			}
		}
		ASSERT (ORDER_type == 18);
		switch (TAG_type (t)) {
	    case type_pre_tag : {
			/* Pre-types */
			BASE_TYPE bt = DEREF_btype (type_pre_rep (t));
			BASE_TYPE kt = (bt & btype_named);
			sp = print_cv (qual, bf, sp);
			if (kt) {
				if (kt == btype_alias) {
					sp = print_id_short (tid, qual_none, bf, sp);
				} else {
					HASHID nm = DEREF_hashid (id_name (tid));
					sp = print_btype (kt, bf, sp);
					sp = print_hashid (nm, 0, 0, bf, sp);
				}
			} else {
				sp = print_btype (bt, bf, sp);
			}
			break;
	    }
	    case type_integer_tag : {
			/* Integral types */
			INT_TYPE it = DEREF_itype (type_integer_rep (t));
			sp = print_cv (qual, bf, sp);
			sp = print_itype (it, bf, sp);
			break;
	    }
	    case type_floating_tag : {
			/* Floating-point types */
			FLOAT_TYPE ft = DEREF_ftype (type_floating_rep (t));
			sp = print_cv (qual, bf, sp);
			sp = print_ftype (ft, bf, sp);
			break;
	    }
	    case type_top_tag : {
			/* Top type */
			sp = print_cv (qual, bf, sp);
			sp = print_ntype (ntype_void, bf, sp);
			break;
	    }
	    case type_bottom_tag : {
			/* Bottom type */
			sp = print_cv (qual, bf, sp);
			sp = print_ntype (ntype_bottom, bf, sp);
			break;
	    }
	    case type_ptr_tag : {
			/* Pointer type */
			TYPE s = DEREF_type (type_ptr_sub (t));
			sp = print_head (s, key, bf, sp);
			if (is_tailed_type (s)) {
				bfprintf (bf, " (*");
			} else {
				if (sp) bfputc (bf, ' ');
				bfputc (bf, '*');
			}
			sp = (qual ? print_cv (qual, bf, 1) : 0);
			break;
	    }
	    case type_ref_tag : {
			/* Reference type */
			TYPE s = DEREF_type (type_ref_sub (t));
			sp = print_head (s, key, bf, sp);
			if (is_tailed_type (s)) {
				bfprintf (bf, " (&");
			} else {
				if (sp) bfputc (bf, ' ');
				bfputc (bf, '&');
			}
			sp = (qual ? print_cv (qual, bf, 1) : 0);
			break;
	    }
	    case type_ptr_mem_tag : {
			/* Pointer to member type */
			TYPE s = DEREF_type (type_ptr_mem_sub (t));
			CLASS_TYPE ct = DEREF_ctype (type_ptr_mem_of (t));
			sp = print_head (s, key, bf, sp);
			if (is_tailed_type (s)) {
				bfprintf (bf, " (");
				sp = 0;
			}
			IGNORE print_ctype (ct, qual_none, 0, bf, sp);
			bfprintf (bf, "::*");
			sp = (qual ? print_cv (qual, bf, 1) : 0);
			break;
	    }
	    case type_func_tag : {
			/* Function type */
			qual = DEREF_cv (type_func_mqual (t));
			if (qual && print_func_linkage) {
				sp = print_linkage (qual, bf, sp);
			}
			if (print_return_type) {
				TYPE r = DEREF_type (type_func_ret (t));
				if (!IS_NULL_type (r)) {
					sp = print_head (r, 0, bf, sp);
				}
			}
			break;
	    }
	    case type_array_tag : {
			/* Array type */
			TYPE s = DEREF_type (type_array_sub (t));
			sp = print_head (s, key, bf, sp);
			break;
	    }
	    case type_bitfield_tag : {
			/* Bitfield type */
			INT_TYPE it = DEREF_itype (type_bitfield_defn (t));
			sp = print_cv (qual, bf, sp);
			sp = print_itype (it, bf, sp);
			break;
	    }
	    case type_compound_tag : {
			/* Class type */
			CLASS_TYPE ct = DEREF_ctype (type_compound_defn (t));
			sp = print_cv (qual, bf, sp);
			sp = print_ctype (ct, qual_none, key, bf, sp);
			break;
	    }
	    case type_enumerate_tag : {
			/* Enumeration type */
			ENUM_TYPE et = DEREF_etype (type_enumerate_defn (t));
			sp = print_cv (qual, bf, sp);
			sp = print_etype (et, key, bf, sp);
			break;
	    }
	    case type_token_tag : {
			/* Tokenised type */
			IDENTIFIER id = DEREF_id (type_token_tok (t));
			LIST (TOKEN) args = DEREF_list (type_token_args (t));
			sp = print_cv (qual, bf, sp);
			sp = print_token (id, qual_none, args, bf, sp);
			break;
	    }
	    case type_templ_tag : {
			/* Template type */
			TYPE s = DEREF_type (type_templ_defn (t));
			TOKEN tok = DEREF_tok (type_templ_sort (t));
			sp = print_sort (tok, 0, bf, sp);
			sp = print_cv (qual, bf, sp);
			sp = print_head (s, 1, bf, sp);
			break;
	    }
	    case type_instance_tag : {
			/* Instance type */
			IDENTIFIER id = DEREF_id (type_name (t));
			sp = print_id_short (id, qual_none, bf, sp);
			break;
	    }
	    default : {
			/* Error type */
			sp = print_cv (qual, bf, sp);
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "<error_type>");
			sp = 1;
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    TYPE TAIL PRINTING
 *
 *    This routine prints the tail of a type (i.e. any array, bitfield or
 *    function components) to the buffer bf.
 */

static int
print_tail(TYPE t, BUFFER *bf, int sp)
{
    if (!IS_NULL_type (t)) {
		IDENTIFIER tid = DEREF_id (type_name (t));
		if (!IS_NULL_id (tid) && print_type_alias) {
			return (sp);
		}
		switch (TAG_type (t)) {
	    case type_ptr_tag :
	    case type_ref_tag : {
			/* Pointer and reference types */
			TYPE s = DEREF_type (type_ptr_etc_sub (t));
			if (is_tailed_type (s)) bfprintf (bf, ")");
			sp = print_tail (s, bf, sp);
			break;
	    }
	    case type_ptr_mem_tag : {
			/* Pointer to member type */
			TYPE s = DEREF_type (type_ptr_mem_sub (t));
			if (is_tailed_type (s)) bfprintf (bf, ")");
			sp = print_tail (s, bf, sp);
			break;
	    }
	    case type_func_tag : {
			/* Function type */
			int prt = print_return_type;
			int ell = DEREF_int (type_func_ellipsis (t));
			CV_SPEC qual = DEREF_cv (type_func_mqual (t));
			LIST (TYPE) p = DEREF_list (type_func_ptypes (t));
			LIST (IDENTIFIER) q = DEREF_list (type_func_pids (t));
			LIST (TYPE) ex = DEREF_list (type_func_except (t));
			sp = 0;
			if (print_member_type) {
				/* Print member parameter types */
				p = DEREF_list (type_func_mtypes (t));
			}
			if (ell & FUNC_WEAK) {
				/* Weak function prototype */
				bfprintf (bf, " WEAK");
			}
			bfprintf (bf, " (");
			if (IS_NULL_list (p) && !(ell & FUNC_ELLIPSIS)) {
				/* There are no parameters */
				if (!(ell & FUNC_NO_PARAMS) && print_c_style) {
					bfprintf (bf, " void");
					sp = 1;
				}
			} else {
				/* Print parameters */
				int pars = print_func_params;
				int dargs = print_default_args;
				if (LENGTH_list (p) != LENGTH_list (q)) dargs = 0;
				print_return_type = 1;
				while (!IS_NULL_list (p)) {
					TYPE s = DEREF_type (HEAD_list (p));
					if (ell & FUNC_PARAMS) s = unpromote_type (s);
					IGNORE print_type (s, bf, 1);
					if (dargs) {
						/* Print default argument */
						IDENTIFIER id = DEREF_id (HEAD_list (q));
						EXP e = DEREF_exp (id_parameter_init (id));
						if (pars) {
							HASHID nm = DEREF_hashid (id_name (id));
							IGNORE print_hashid (nm, 0, 0, bf, 1);
						}
						if (!IS_NULL_exp (e)) {
							bfprintf (bf, " = ");
							IGNORE print_exp (e, 0, bf, 0);
						}
						q = TAIL_list (q);
					}
					p = TAIL_list (p);
					if (!IS_NULL_list (p)) {
						bfputc (bf, ',');
					} else if (ell & FUNC_ELLIPSIS) {
						bfputc (bf, ',');
					}
					sp = 1;
				}
				if (ell & FUNC_ELLIPSIS) {
					bfprintf (bf, " ...");
					sp = 1;
				}
				print_return_type = prt;
			}
			if (sp) bfputc (bf, ' ');
			bfputc (bf, ')');
			sp = print_cv (qual, bf, 1);
			if (prt) {
				TYPE r = DEREF_type (type_func_ret (t));
				if (!IS_NULL_type (r)) {
					sp = print_tail (r, bf, sp);
				}
			}
			if (!EQ_list (ex, univ_type_set) && print_except) {
				/* Print exception specifier */
				sp = print_lex (lex_throw, bf, sp);
				sp = print_type_list (ex, bf, sp);
			}
			break;
	    }
	    case type_array_tag : {
			/* Array type */
			TYPE s = DEREF_type (type_array_sub (t));
			NAT n = DEREF_nat (type_array_size (t));
			bfprintf (bf, " [");
			IGNORE print_nat (n, 0, bf, 0);
			bfprintf (bf, "]");
			sp = print_tail (s, bf, 1);
			break;
	    }
	    case type_bitfield_tag : {
			/* Bitfield type */
			INT_TYPE it = DEREF_itype (type_bitfield_defn (t));
			NAT n = DEREF_nat (itype_bitfield_size (it));
			bfputc (bf, ' ');
			bfputc (bf, print_bitfield_sep);
			IGNORE print_nat (n, 0, bf, 1);
			break;
	    }
	    case type_templ_tag : {
			/* Template type */
			TYPE s = DEREF_type (type_templ_defn (t));
			sp = print_tail (s, bf, sp);
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    PRINT A TYPE
 *
 *    This routine prints the type t to the buffer bf.  Note that this is in
 *    two passes - the first prints the head of the type, comprising the
 *    base type and any pointer or reference qualifiers, while the second
 *    prints the tail of the type, comprising any array or function qualifiers.
 */

int
print_type(TYPE t, BUFFER *bf, int sp)
{
    sp = print_head (t, print_c_style, bf, sp);
    sp = print_tail (t, bf, sp);
    return (sp);
}


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

int
print_type_list(LIST (TYPE) p, BUFFER *bf, int sp)
{
    if (sp) bfputc (bf, ' ');
    bfputc (bf, '(');
    if (!IS_NULL_list (p)) {
		for (; ;) {
			TYPE t = DEREF_type (HEAD_list (p));
			IGNORE print_type (t, bf, 1);
			p = TAIL_list (p);
			if (IS_NULL_list (p)) break;
			bfputc (bf, ',');
		}
		bfputc (bf, ' ');
    }
    bfputc (bf, ')');
    return (1);
}


/*
 *    OFFSET PRINTING
 *
 *    This routine prints an offset to the buffer bf.
 */

int
print_offset(OFFSET off, BUFFER *bf, int sp)
{
    if (!IS_NULL_off (off)) {
		switch (TAG_off (off)) {
	    case off_base_tag : {
			GRAPH gr = DEREF_graph (off_base_graph (off));
			sp = print_graph (gr, 0, bf, sp);
			break;
	    }
	    case off_deriv_tag : {
			GRAPH gr = DEREF_graph (off_deriv_graph (off));
			sp = print_graph (gr, 0, bf, sp);
			break;
	    }
	    case off_member_tag : {
			IDENTIFIER id = DEREF_id (off_member_id (off));
			sp = print_id_short (id, qual_none, bf, sp);
			break;
	    }
	    case off_token_tag : {
			IDENTIFIER id = DEREF_id (off_token_tok (off));
			LIST (TOKEN) args = DEREF_list (off_token_args (off));
			sp = print_token (id, qual_none, args, bf, sp);
			break;
	    }
	    default : {
			static unsigned long off_no = 0;
#ifdef RUNTIME
			if (debugging) {
				/* Debug offset printing routine */
				sp = print_offset_aux (off, bf, sp);
				break;
			}
#endif
			if (sp) bfputc (bf, ' ');
			bfprintf (bf, "<off%lu>", ++off_no);
			sp = 1;
			break;
	    }
		}
    }
    return (sp);
}


/*
 *    FIND A LINE WITHIN THE CURRENT FILE BUFFER
 *
 *    This routine checks whether the start of line n lies within the
 *    current file buffer.  If so it returns a pointer to the start of
 *    the line.  m gives the line number of the current position.
 */

static string
find_buffer_line(unsigned long n, unsigned long m)
{
    string p = input_posn - 1;
    if (n <= m) {
		/* Scan backwards */
		while (p >= input_start) {
			character c = *(p--);
			if (c == char_newline) {
				if (n == m) return (p + 2);
				m--;
			}
		}
		if (m == 1) {
			/* Allow falling off start */
			return (input_start);
		}
    } else {
		while (p < input_end) {
			character c = *(p++);
			if (c == char_newline) {
				if (n == m) return (p);
				m++;
			}
		}
		if (p == input_eof) {
			/* Allow falling off end */
			return (p);
		}
    }
    return (NULL);
}


/*
 *    FIND A LOCATION WITHIN THE CURRENT FILE BUFFER
 *
 *    This routine checks whether the n lines following line ln of file fn
 *    lie within the input buffer.  If so it returns the position of the
 *    first line within the buffer.
 */

static string
find_buffer_loc(string fn, unsigned long ln, unsigned long n)
{
    if (input_start && !bad_crt_loc) {
		PTR (POSITION) pm = crt_loc.posn;
		if (!IS_NULL_ptr (pm)) {
			string fm = DEREF_string (posn_input (pm));
			if (ustreq (fn, fm)) {
				unsigned long om = DEREF_ulong (posn_offset (pm));
				unsigned long lm = crt_loc.line - om;
				string p = find_buffer_line (ln, lm);
				if (p) {
					string q = find_buffer_line (ln + n, lm);
					if (q) return (p);
				}
			}
		}
    }
    return (NULL);
}


/*
 *    PRINT SOURCE LINES
 *
 *    This routine prints a number of lines from the input buffer centred
 *    on the location loc to the file f.  If loc does not correspond to a
 *    position within the current buffer then no text is output.
 */

void
print_source(LOCATION *loc, int lines, int full, const char *pre, FILE *f)
{
    PTR (POSITION) pn = loc->posn;
    if (lines > 0 && !IS_NULL_ptr (pn)) {
		string p;
		int nl = 0;
		FILE *g = NULL;
		const char *mark = "!!!!";
		unsigned long n = (unsigned long) lines;
		unsigned long b = n / 2;
		string fn = DEREF_string (posn_input (pn));
		unsigned long on = DEREF_ulong (posn_offset (pn));
		unsigned long ln = loc->line - on;
		unsigned long cn = loc->column;
		unsigned long lc = ln;
		unsigned long cc = 0;
		if (ln <= b) {
			ln = 1;
		} else {
			ln -= b;
		}
		
		/* Find start of source */
		p = find_buffer_loc (fn, ln, n);
		if (p == NULL) {
			g = fopen (strlit (fn), "r");
			if (g) {
				/* Skip to correct line in file */
				unsigned long lm = 1;
				while (lm != ln) {
					int c = fgetc (g);
					if (c == EOF) {
						fclose_v (g);
						g = NULL;
						break;
					}
					if (c == '\n') lm++;
				}
			}
		}
		
		/* Print source */
		if (full) {
			if (pre) fputs_v (pre, f);
			fn = DEREF_string (posn_file (pn));
			fprintf_v (f, "FILE: %s\n", strlit (fn));
		}
		while (n) {
			int c;
			if (nl == 0) {
				if (pre) fputs_v (pre, f);
				if (full) fprintf_v (f, "%lu:\t", ln + on);
				nl = 1;
			}
			if (ln == lc && cn == cc) {
				fputs_v (mark, f);
				mark = NULL;
			}
			if (p) {
				/* Read from buffer */
				if (p >= input_end) break;
				c = (int) *(p++);
			} else if (g) {
				/* Read from file */
				c = fgetc (g);
				if (c == EOF) break;
			} else {
				break;
			}
			if (c == char_newline) {
				/* Newline characters */
				if (ln == lc && mark) {
					fputs_v (mark, f);
					mark = NULL;
				}
				if (--n == 0) break;
				nl = 0;
				ln++;
				cc = 0;
			} else {
				cc++;
			}
			fputc_v ((int) c, f);
		}
		if (mark) {
			fputs_v (mark, f);
			nl = 1;
		}
		if (nl) fputc_v ('\n', f);
		if (g) fclose_v (g);
    }
    return;
}
