/**************************************************************************

    util.c

    Copyright (C) 1998, 1999 Andrew T. Veliath

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Id$

***************************************************************************/
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif

#include "rename.h"
#include "util.h"

#ifdef G_OS_WIN32
#include <fcntl.h>
#include <direct.h>
#define popen _popen
#define pclose _pclose
#endif

const char *IDL_tree_type_names[] = {
	"IDLN_NONE",
	"IDLN_ANY",
	"IDLN_LIST",
	"IDLN_GENTREE",
	"IDLN_INTEGER",
	"IDLN_STRING",
	"IDLN_WIDE_STRING",
	"IDLN_CHAR",
	"IDLN_WIDE_CHAR",
	"IDLN_FIXED",
	"IDLN_FLOAT",
	"IDLN_BOOLEAN",
	"IDLN_IDENT",
	"IDLN_TYPE_DCL",
	"IDLN_CONST_DCL",
	"IDLN_EXCEPT_DCL",
	"IDLN_ATTR_DCL",
	"IDLN_OP_DCL",
	"IDLN_PARAM_DCL",
	"IDLN_FORWARD_DCL",
	"IDLN_TYPE_INTEGER",
	"IDLN_TYPE_FLOAT",
	"IDLN_TYPE_FIXED",
	"IDLN_TYPE_CHAR",
	"IDLN_TYPE_WIDE_CHAR",
	"IDLN_TYPE_STRING",
	"IDLN_TYPE_WIDE_STRING",
	"IDLN_TYPE_BOOLEAN",
	"IDLN_TYPE_OCTET",
	"IDLN_TYPE_ANY",
	"IDLN_TYPE_OBJECT",
	"IDLN_TYPE_TYPECODE",
	"IDLN_TYPE_ENUM",
	"IDLN_TYPE_SEQUENCE",
	"IDLN_TYPE_ARRAY",
	"IDLN_TYPE_STRUCT",
	"IDLN_TYPE_UNION",
	"IDLN_MEMBER",
	"IDLN_NATIVE",
	"IDLN_CASE_STMT",
	"IDLN_INTERFACE",
	"IDLN_MODULE",
	"IDLN_BINOP",
	"IDLN_UNARYOP",
	"IDLN_CODEFRAG",
	/* IDLN_LAST */
};

int					__IDL_check_type_casts = FALSE;
#ifndef HAVE_CPP_PIPE_STDIN
const char *				__IDL_tmp_filename = NULL;
#endif
const char *				__IDL_real_filename = NULL;
char *					__IDL_cur_filename = NULL;
int					__IDL_cur_line;
GHashTable *				__IDL_filename_hash;
IDL_fileinfo *				__IDL_cur_fileinfo;
GHashTable *				__IDL_structunion_ht;
int					__IDL_inhibits;
int					__IDL_typecodes_as_tok;
int					__IDL_pidl;
IDL_tree				__IDL_root;
IDL_ns					__IDL_root_ns;
int					__IDL_is_okay;
int					__IDL_is_parsing;
unsigned long				__IDL_flags;
unsigned long				__IDL_flagsi;
gpointer				__IDL_inputcb_user_data;
IDL_input_callback			__IDL_inputcb;
GSList *				__IDL_new_ident_comments;
static int				__IDL_max_msg_level;
static int				__IDL_nerrors, __IDL_nwarnings;
static IDL_msg_callback			__IDL_msgcb;

/* Case-insensitive version of g_str_hash */
guint IDL_strcase_hash (gconstpointer v)
{
	const char *p;
	guint h = 0, g;

	for (p = (char *) v; *p != '\0'; ++p) {
		h = (h << 4) + isupper ((int)*p) ? tolower (*p) : *p;
		if ((g = h & 0xf0000000)) {
			h = h ^ (g >> 24);
			h = h ^ g;
		}
	}

	return h /* % M */;
}

gint IDL_strcase_equal (gconstpointer a, gconstpointer b)
{
	return g_ascii_strcasecmp (a, b) == 0;
}

gint IDL_strcase_cmp (gconstpointer a, gconstpointer b)
{
	return g_ascii_strcasecmp (a, b);
}

static int my_strcmp (IDL_tree p, IDL_tree q)
{
	const char *a = IDL_IDENT (p).str;
	const char *b = IDL_IDENT (q).str;
	int cmp = IDL_strcase_cmp (a, b);

	if (__IDL_is_parsing &&
	    cmp == 0 &&
	    strcmp (a, b) != 0 &&
	    !(IDL_IDENT (p)._flags & IDLF_IDENT_CASE_MISMATCH_HIT ||
	      IDL_IDENT (q)._flags & IDLF_IDENT_CASE_MISMATCH_HIT)) {
		IDL_tree_warning (p, IDL_WARNING1, "Case mismatch between `%s'", a);
		IDL_tree_warning (q, IDL_WARNING1, "and `%s'", b);
		yywarning (IDL_WARNING1,
			   "(Identifiers should be case-consistent after initial declaration)");
		IDL_IDENT (p)._flags |= IDLF_IDENT_CASE_MISMATCH_HIT;
		IDL_IDENT (q)._flags |= IDLF_IDENT_CASE_MISMATCH_HIT;
	}

	return cmp;
}

guint IDL_ident_hash (gconstpointer v)
{
	return IDL_strcase_hash (IDL_IDENT ((IDL_tree) v).str);
}

gint IDL_ident_equal (gconstpointer a, gconstpointer b)
{
	return my_strcmp ((IDL_tree) a, (IDL_tree) b) == 0;
}

gint IDL_ident_cmp (gconstpointer a, gconstpointer b)
{
	return my_strcmp ((IDL_tree) a, (IDL_tree) b);
}

const char *IDL_get_libver_string (void)
{
	return LIBIDL_VERSION;
}

const char *IDL_get_IDLver_string (void)
{
	return "2.2";
}

static void IDL_tree_optimize (IDL_tree *p, IDL_ns ns)
{
	if (!(__IDL_flags & IDLF_IGNORE_FORWARDS))
		IDL_tree_process_forward_dcls (p, ns);
	if (!(__IDL_flags & IDLF_INHIBIT_TAG_ONLY))
		IDL_tree_remove_inhibits (p, ns);
	IDL_tree_remove_empty_modules (p, ns);
}

static void IDL_parse_setup(unsigned long parse_flags, int max_msg_level) {
	if (parse_flags & IDLF_XPIDL)
		parse_flags |= IDLF_PROPERTIES;

	__IDL_max_msg_level = max_msg_level;
	__IDL_nerrors = __IDL_nwarnings = 0;
	__IDL_inhibits = 0;
	__IDL_typecodes_as_tok = (parse_flags & IDLF_TYPECODES) ? 1 : 0;
	__IDL_pidl = (parse_flags & IDLF_XPIDL) ? 1 : 0;
	__IDL_flags = parse_flags;
	__IDL_flagsi = 0;
	__IDL_is_parsing = TRUE;
	__IDL_is_okay = TRUE;
	__IDL_new_ident_comments = NULL;
}

int IDL_parse_filename (const char *filename, const char *cpp_args,
			IDL_msg_callback msg_cb, IDL_tree *tree, IDL_ns *ns,
			unsigned long parse_flags, int max_msg_level)
{
	extern void __IDL_lex_init (void);
	extern void __IDL_lex_cleanup (void);
	extern int yyparse (void);
	extern FILE *__IDL_in;
	FILE *input;
	char *cmd;
#ifdef HAVE_CPP_PIPE_STDIN
	char *fmt = CPP_PROGRAM " - %s%s %s < \"%s\" %s";
	char *wd;
#else
	char *fmt = CPP_PROGRAM " -I%s %s \"%s\" %s";
	char cwd[2048];
#ifdef HAVE_SYMLINK
	char *s, *tmpfilename;
	gchar *linkto;
#else
	const char *tmpfilename;
#endif
#endif

#ifndef G_OS_WIN32
	char *cpperrs = (parse_flags & IDLF_SHOW_CPP_ERRORS) 
	  ? "" : "2>/dev/null";
#else
	char *cpperrs = "";
#endif
	GSList *slist;
	int rv;

#if 0 && defined(YYDEBUG)
	{
		extern int __IDL_debug;
		__IDL_debug = 1;
	}
#endif

	if (!filename || !tree) {
		errno = EINVAL;
		return -1;
	}

#ifdef HAVE_ACCESS
	if (access (filename, R_OK))
		return -1;
#endif

#ifdef HAVE_CPP_PIPE_STDIN
	wd = g_path_get_dirname (filename);

	cmd = g_strdup_printf (fmt, "-I", wd, cpp_args ? cpp_args : "",
			       filename, cpperrs);

	g_free (wd);
#else
	if (!getcwd (cwd, sizeof (cwd)))
		return -1;

#ifdef HAVE_SYMLINK
	s = tmpnam (NULL);
	if (s == NULL)
		return -1;

	if (g_path_is_absolute (filename)) {
		linkto = g_strdup (filename);
	} else {
		linkto = g_strconcat (cwd, "/", filename, NULL);
	}

	tmpfilename = g_strconcat (s, ".c", NULL);

	if (symlink (linkto, tmpfilename) < 0) {
		g_free (linkto);
		g_free (tmpfilename);
		return -1;
	}
	g_free (linkto);
#else
	tmpfilename = filename;
#endif

	cmd = g_strdup_printf (fmt, cwd, cpp_args ? cpp_args : "",
			       tmpfilename, cpperrs);
#endif

       /* Many versions of cpp do evil translating internal
        * strings, producing bogus output, so clobber LC_ALL */
	putenv ("LC_ALL=C");

#ifdef HAVE_POPEN
#if defined (G_OS_WIN32) && !defined (_MSC_VER)
	if (!(parse_flags & IDLF_SHOW_CPP_ERRORS)) {
		int save_stderr = dup (2);
		int null = open ("NUL:", O_WRONLY);
		dup2 (null, 2);
		input = popen (cmd, "r");
		close (2);
		close (null);
		dup2 (save_stderr, 2);
		close (save_stderr);
	} else
		input = popen (cmd, "r");
#else
	input = popen (cmd, "r");
#endif
#else
#error Must have popen
#endif
	g_free (cmd);

	if (input == NULL || ferror (input)) {
#if !defined (HAVE_CPP_PIPE_STDIN) && defined (HAVE_SYMLINK)
		g_free (tmpfilename);
#endif
		return IDL_ERROR;
	}

	IDL_parse_setup(parse_flags, max_msg_level);

	__IDL_in = input;
	__IDL_msgcb = msg_cb;
	__IDL_root_ns = IDL_ns_new ();
	__IDL_lex_init ();

	__IDL_real_filename = filename;
#ifndef HAVE_CPP_PIPE_STDIN
	__IDL_tmp_filename = tmpfilename;
#endif
	__IDL_filename_hash = IDL_NS (__IDL_root_ns).filename_hash;
	__IDL_structunion_ht = g_hash_table_new (g_direct_hash, g_direct_equal);
	rv = yyparse ();
	g_hash_table_destroy (__IDL_structunion_ht);
	__IDL_is_parsing = FALSE;
	__IDL_lex_cleanup ();
	__IDL_parser_reset ();
	__IDL_real_filename = NULL;
#ifndef HAVE_CPP_PIPE_STDIN
	__IDL_tmp_filename = NULL;
#endif
#ifdef HAVE_POPEN
	pclose (input);
#else
	fclose (input);
#endif
#if !defined (HAVE_CPP_PIPE_STDIN) && defined (HAVE_SYMLINK)
	unlink (tmpfilename);
	g_free (tmpfilename);
#endif
	for (slist = __IDL_new_ident_comments; slist; slist = slist->next)
		g_free (slist->data);
	g_slist_free (__IDL_new_ident_comments);

	if (__IDL_root != NULL) {
		IDL_tree_optimize (&__IDL_root, __IDL_root_ns);

		if (__IDL_root == NULL)
			yyerror ("File empty after optimization");
	}

	__IDL_msgcb = NULL;

	if (rv != 0 || !__IDL_is_okay) {
		*tree = NULL;

		if (ns)
			*ns = NULL;

		return IDL_ERROR;
	}

	if (__IDL_flags & IDLF_PREFIX_FILENAME)
		IDL_ns_prefix (__IDL_root_ns, filename);

	*tree = __IDL_root;

	if (ns)
		*ns = __IDL_root_ns;
	else
		IDL_ns_free (__IDL_root_ns);

	return IDL_SUCCESS;
}

int IDL_parse_filename_with_input (const char *filename,
				   IDL_input_callback input_cb,
				   gpointer input_cb_user_data,
				   IDL_msg_callback msg_cb,
				   IDL_tree *tree, IDL_ns *ns,
				   unsigned long parse_flags,
				   int max_msg_level)
{
	extern void __IDL_lex_init (void);
	extern void __IDL_lex_cleanup (void);
	extern int yyparse (void);
	union IDL_input_data data;
	GSList *slist;
	int rv;

	if (!filename || !input_cb || !tree) {
		errno = EINVAL;
		return -1;
	}

	IDL_parse_setup(parse_flags, max_msg_level);

	__IDL_msgcb = msg_cb;
	__IDL_root_ns = IDL_ns_new ();

	__IDL_lex_init ();
	__IDL_inputcb = input_cb;
	__IDL_inputcb_user_data = input_cb_user_data;

	__IDL_real_filename = filename;
#ifndef HAVE_CPP_PIPE_STDIN
	__IDL_tmp_filename = NULL;
#endif
	__IDL_filename_hash = IDL_NS (__IDL_root_ns).filename_hash;
	data.init.filename = filename;
	if ((*__IDL_inputcb) (
		IDL_INPUT_REASON_INIT, &data, __IDL_inputcb_user_data)) {
		IDL_ns_free (__IDL_root_ns);
		__IDL_lex_cleanup ();
		__IDL_real_filename = NULL;
		return -1;
	}
	__IDL_structunion_ht = g_hash_table_new (g_direct_hash, g_direct_equal);
	rv = yyparse ();
	g_hash_table_destroy (__IDL_structunion_ht);
	__IDL_is_parsing = FALSE;
	__IDL_lex_cleanup ();
	__IDL_parser_reset ();
	__IDL_real_filename = NULL;
	for (slist = __IDL_new_ident_comments; slist; slist = slist->next)
		g_free (slist->data);
	g_slist_free (__IDL_new_ident_comments);

	if (__IDL_root != NULL) {
		IDL_tree_optimize (&__IDL_root, __IDL_root_ns);

		if (__IDL_root == NULL)
			yyerror ("File empty after optimization");
	}

	__IDL_msgcb = NULL;

	if (rv != 0 || !__IDL_is_okay) {
		*tree = NULL;

		if (ns)
			*ns = NULL;

		(*__IDL_inputcb) (
			IDL_INPUT_REASON_ABORT, NULL, __IDL_inputcb_user_data);

		return IDL_ERROR;
	}

	(*__IDL_inputcb) (IDL_INPUT_REASON_FINISH, NULL, __IDL_inputcb_user_data);

	if (__IDL_flags & IDLF_PREFIX_FILENAME)
		IDL_ns_prefix (__IDL_root_ns, filename);

	*tree = __IDL_root;

	if (ns)
		*ns = __IDL_root_ns;
	else
		IDL_ns_free (__IDL_root_ns);

	return IDL_SUCCESS;
}

void yyerrorl (const char *s, int ofs)
{
	int line = __IDL_cur_line - 1 + ofs;
	gchar *freeme = NULL, *filename = NULL;

	if (__IDL_cur_filename) {
#ifdef HAVE_CPP_PIPE_STDIN
		filename = __IDL_cur_filename;
#else
		freeme = filename = g_path_get_basename (__IDL_cur_filename);
#endif
	} else
		line = -1;

	++__IDL_nerrors;
	__IDL_is_okay = FALSE;

	/* Errors are counted, even if not printed */
	if (__IDL_max_msg_level < IDL_ERROR) {
		g_free (freeme);
		return;
	}

	if (__IDL_msgcb)
		(*__IDL_msgcb)(IDL_ERROR, __IDL_nerrors, line, filename, s);
	else {
		if (line > 0)
			fprintf (stderr, "%s:%d: Error: %s\n", filename, line, s);
		else
			fprintf (stderr, "Error: %s\n", s);
	}
	g_free (freeme);
}

void yywarningl (int level, const char *s, int ofs)
{
	int line = __IDL_cur_line - 1 + ofs;
	gchar *freeme = NULL, *filename = NULL;

	/* Unprinted warnings are not counted */
	if (__IDL_max_msg_level < level)
		return;

	if (__IDL_cur_filename) {
#ifdef HAVE_CPP_PIPE_STDIN
		filename = __IDL_cur_filename;
#else
		freeme = filename = g_path_get_basename (__IDL_cur_filename);
#endif
	} else
		line = -1;

	++__IDL_nwarnings;

	if (__IDL_msgcb)
		(*__IDL_msgcb)(level, __IDL_nwarnings, line, filename, s);
	else {
		if (line > 0)
			fprintf (stderr, "%s:%d: Warning: %s\n", filename, line, s);
		else
			fprintf (stderr, "Warning: %s\n", s);
	}
	g_free (freeme);
}

void yyerror (const char *s)
{
	yyerrorl (s, 0);
}

void yywarning (int level, const char *s)
{
	yywarningl (level, s, 0);
}

void yyerrorlv (const char *fmt, int ofs, ...)
{
	gchar *msg;
	va_list args;

	va_start (args, ofs);

	msg = g_strdup_vprintf (fmt, args);
	yyerrorl (msg, ofs);

	va_end (args);

	g_free (msg);
}

void yywarninglv (int level, const char *fmt, int ofs, ...)
{
	gchar *msg;
	va_list args;

	va_start (args, ofs);

	msg = g_strdup_vprintf (fmt, args);
	yywarningl (level, msg, ofs);

	va_end (args);

	g_free (msg);
}

void yyerrorv (const char *fmt, ...)
{
	gchar *msg;
	va_list args;

	va_start (args, fmt);

	msg = g_strdup_vprintf (fmt, args);
	yyerror (msg);

	va_end (args);

	g_free (msg);
}

void yywarningv (int level, const char *fmt, ...)
{
	gchar *msg;
	va_list args;

	va_start (args, fmt);

	msg = g_strdup_vprintf (fmt, args);
	yywarning (level, msg);

	va_end (args);

	g_free (msg);
}

void IDL_tree_error (IDL_tree p, const char *fmt, ...)
{
	char *file_save = __IDL_cur_filename;
	int line_save = __IDL_cur_line;
	gchar *msg;
	va_list args;

	if (p) {
		__IDL_cur_filename = p->_file;
		__IDL_cur_line = p->_line;
	} else {
		__IDL_cur_filename = NULL;
		__IDL_cur_line = -1;
	}

	va_start (args, fmt);

	msg = g_strdup_vprintf (fmt, args);
	yyerror (msg);

	va_end (args);

	g_free (msg);

	__IDL_cur_filename = file_save;
	__IDL_cur_line = line_save;
}

void IDL_tree_warning (IDL_tree p, int level, const char *fmt, ...)
{
	char *file_save = __IDL_cur_filename;
	int line_save = __IDL_cur_line;
	gchar *msg;
	va_list args;

	if (p) {
		__IDL_cur_filename = p->_file;
		__IDL_cur_line = p->_line;
	} else {
		__IDL_cur_filename = NULL;
		__IDL_cur_line = -1;
	}

	va_start (args, fmt);

	msg = g_strdup_vprintf (fmt, args);
	yywarning (level, msg);

	va_end (args);

	g_free (msg);

	__IDL_cur_filename = file_save;
	__IDL_cur_line = line_save;
}

int IDL_tree_get_node_info (IDL_tree p, char **what, char **who)
{
	int dienow = 0;

	assert (what != NULL);
	assert (who != NULL);

	switch (IDL_NODE_TYPE (p)) {
	case IDLN_TYPE_STRUCT:
		*what = "structure definition";
		*who = IDL_IDENT (IDL_TYPE_STRUCT (p).ident).str;
		break;

	case IDLN_TYPE_UNION:
		*what = "union definition";
		*who = IDL_IDENT (IDL_TYPE_UNION (p).ident).str;
		break;

	case IDLN_TYPE_ARRAY:
		*what = "array";
		*who = IDL_IDENT (IDL_TYPE_ARRAY (p).ident).str;
		break;

	case IDLN_TYPE_ENUM:
		*what = "enumeration definition";
		*who = IDL_IDENT (IDL_TYPE_ENUM (p).ident).str;
		break;

	case IDLN_IDENT:
		*what = "identifier";
		*who = IDL_IDENT (p).str;
		break;

	case IDLN_TYPE_DCL:
		*what = "type definition";
		assert (IDL_TYPE_DCL (p).dcls != NULL);
		assert (IDL_NODE_TYPE (IDL_TYPE_DCL (p).dcls) == IDLN_LIST);
		assert (IDL_LIST (IDL_TYPE_DCL (p).dcls)._tail != NULL);
		assert (IDL_NODE_TYPE (IDL_LIST (IDL_TYPE_DCL (p).dcls)._tail) == IDLN_LIST);
		*who = IDL_IDENT (IDL_LIST (IDL_LIST (IDL_TYPE_DCL (p).dcls)._tail).data).str;
		break;

	case IDLN_MEMBER:
		*what = "member declaration";
		assert (IDL_MEMBER (p).dcls != NULL);
		assert (IDL_NODE_TYPE (IDL_MEMBER (p).dcls) == IDLN_LIST);
		assert (IDL_LIST (IDL_MEMBER (p).dcls)._tail != NULL);
		assert (IDL_NODE_TYPE (IDL_LIST (IDL_MEMBER (p).dcls)._tail) == IDLN_LIST);
		*who = IDL_IDENT (IDL_LIST (IDL_LIST (IDL_MEMBER (p).dcls)._tail).data).str;
		break;

	case IDLN_NATIVE:
		*what = "native declaration";
		assert (IDL_NATIVE (p).ident != NULL);
		assert (IDL_NODE_TYPE (IDL_NATIVE (p).ident) == IDLN_IDENT);
		*who = IDL_IDENT (IDL_NATIVE (p).ident).str;
		break;

	case IDLN_LIST:
		if (!IDL_LIST (p).data)
			break;
		dienow = IDL_tree_get_node_info (IDL_LIST (p).data, what, who);
		break;

	case IDLN_ATTR_DCL:
		*what = "interface attribute";
		assert (IDL_ATTR_DCL (p).simple_declarations != NULL);
		assert (IDL_NODE_TYPE (IDL_ATTR_DCL (p).simple_declarations) == IDLN_LIST);
		assert (IDL_LIST (IDL_ATTR_DCL (p).simple_declarations)._tail != NULL);
		assert (IDL_NODE_TYPE (IDL_LIST (
			IDL_ATTR_DCL (p).simple_declarations)._tail) == IDLN_LIST);
		*who = IDL_IDENT (IDL_LIST (IDL_LIST (
			IDL_ATTR_DCL (p).simple_declarations)._tail).data).str;
		break;

	case IDLN_PARAM_DCL:
		*what = "operation parameter";
		assert (IDL_PARAM_DCL (p).simple_declarator != NULL);
		assert (IDL_NODE_TYPE (IDL_PARAM_DCL (p).simple_declarator) == IDLN_IDENT);
		*who = IDL_IDENT (IDL_PARAM_DCL (p).simple_declarator).str;
		break;

	case IDLN_CONST_DCL:
		*what = "constant declaration for";
		*who = IDL_IDENT (IDL_CONST_DCL (p).ident).str;
		break;

	case IDLN_EXCEPT_DCL:
		*what = "exception";
		*who = IDL_IDENT (IDL_EXCEPT_DCL (p).ident).str;
		break;

	case IDLN_OP_DCL:
		*what = "interface operation";
		*who = IDL_IDENT (IDL_OP_DCL (p).ident).str;
		break;

	case IDLN_MODULE:
		*what = "module";
		*who = IDL_IDENT (IDL_MODULE (p).ident).str;
		break;

	case IDLN_FORWARD_DCL:
		*what = "forward declaration";
		*who = IDL_IDENT (IDL_FORWARD_DCL (p).ident).str;
		break;

	case IDLN_INTERFACE:
		*what = "interface";
		*who = IDL_IDENT (IDL_INTERFACE (p).ident).str;
		break;

	default:
		g_warning ("Node type: %s\n", IDL_NODE_TYPE_NAME (p));
		*what = "unknown (internal error)";
		break;
	}

	return dienow;
}

static IDL_tree IDL_node_new (IDL_tree_type type)
{
	IDL_tree p;

	p = g_new0 (IDL_tree_node, 1);
	if (p == NULL) {
		yyerror ("IDL_node_new: memory exhausted");
		return NULL;
	}

	IDL_NODE_TYPE (p) = type;
	IDL_NODE_REFS (p) = 1;

	p->_file = __IDL_cur_filename;
	p->_line = __IDL_cur_line;

	return p;
}

void __IDL_assign_up_node (IDL_tree up, IDL_tree node)
{
	if (node == NULL)
		return;

	assert (node != up);

	switch (IDL_NODE_TYPE (node)) {
	case IDLN_LIST:
		if (IDL_NODE_UP (node) == NULL)
			for (; node != NULL; node = IDL_LIST (node).next)
				IDL_NODE_UP (node) = up;
		break;

	default:
		if (IDL_NODE_UP (node) == NULL)
			IDL_NODE_UP (node) = up;
		break;
	}
}

void __IDL_assign_location (IDL_tree node, IDL_tree from_node)
{
	assert (node != NULL);

	if (from_node) {
		node->_file = from_node->_file;
		node->_line = from_node->_line;
	}
}

void __IDL_assign_this_location (IDL_tree node, char *filename, int line)
{
	assert (node != NULL);

	node->_file = filename;
	node->_line = line;
}

IDL_tree IDL_list_new (IDL_tree data)
{
	IDL_tree p = IDL_node_new (IDLN_LIST);

	__IDL_assign_up_node (p, data);
	IDL_LIST (p).data = data;
	IDL_LIST (p)._tail = p;

	return p;
}

IDL_tree IDL_list_concat (IDL_tree orig, IDL_tree append)
{
	IDL_tree p;

	if (orig == NULL)
		return append;

	if (append == NULL)
		return orig;

	IDL_LIST (IDL_LIST (orig)._tail).next = append;
	IDL_LIST (append).prev = IDL_LIST (orig)._tail;
	IDL_LIST (orig)._tail = IDL_LIST (append)._tail;

	/* Set tails on original */
	for (p = IDL_LIST (orig).next; p && p != append; p = IDL_LIST (p).next)
		IDL_LIST (p)._tail = IDL_LIST (orig)._tail;

	/* Set up nodes on appended list */
	for (p = append; p; p = IDL_LIST (p).next)
		IDL_NODE_UP (p) = IDL_NODE_UP (orig);

	return orig;
}

IDL_tree IDL_list_remove (IDL_tree list, IDL_tree p)
{
	IDL_tree new_list = list;

	if (IDL_LIST (p).prev == NULL) {
		assert (list == p);
		new_list = IDL_LIST (p).next;
		if (new_list)
			IDL_LIST (new_list).prev = NULL;
	} else {
		IDL_tree prev = IDL_LIST (p).prev;
		IDL_tree next = IDL_LIST (p).next;

		IDL_LIST (prev).next = next;
		if (next)
			IDL_LIST (next).prev = prev;
	}

	IDL_LIST (p).prev = NULL;
	IDL_LIST (p).next = NULL;
	IDL_LIST (p)._tail = p;

	/* Not all tails updated... */

	return new_list;
}

IDL_tree IDL_gentree_new (GHashFunc hash_func, GCompareFunc key_compare_func, IDL_tree data)
{
	IDL_tree p = IDL_node_new (IDLN_GENTREE);

	__IDL_assign_up_node (p, data);
	IDL_GENTREE (p).data = data;
	IDL_GENTREE (p).hash_func = hash_func;
	IDL_GENTREE (p).key_compare_func = key_compare_func;
	IDL_GENTREE (p).siblings = g_hash_table_new (hash_func, key_compare_func);
	IDL_GENTREE (p).children = g_hash_table_new (hash_func, key_compare_func);

	g_hash_table_insert (IDL_GENTREE (p).siblings, data, p);

	return p;
}

IDL_tree IDL_gentree_new_sibling (IDL_tree from, IDL_tree data)
{
	IDL_tree p = IDL_node_new (IDLN_GENTREE);

	__IDL_assign_up_node (p, data);
	IDL_GENTREE (p).data = data;
	IDL_GENTREE (p).hash_func = IDL_GENTREE (from).hash_func;
	IDL_GENTREE (p).key_compare_func = IDL_GENTREE (from).key_compare_func;
	IDL_GENTREE (p).siblings = IDL_GENTREE (from).siblings;
	IDL_GENTREE (p).children = g_hash_table_new (IDL_GENTREE (from).hash_func,
						     IDL_GENTREE (from).key_compare_func);

	return p;
}

IDL_tree IDL_integer_new (IDL_longlong_t value)
{
	IDL_tree p = IDL_node_new (IDLN_INTEGER);

	IDL_INTEGER (p).value = value;

	return p;
}

IDL_tree IDL_string_new (char *value)
{
	IDL_tree p = IDL_node_new (IDLN_STRING);

	IDL_STRING (p).value = value;

	return p;
}

IDL_tree IDL_wide_string_new (wchar_t *value)
{
	IDL_tree p = IDL_node_new (IDLN_WIDE_STRING);

	IDL_WIDE_STRING (p).value = value;

	return p;
}

IDL_tree IDL_char_new (char *value)
{
	IDL_tree p = IDL_node_new (IDLN_CHAR);

	IDL_CHAR (p).value = value;

	return p;
}

IDL_tree IDL_wide_char_new (wchar_t *value)
{
	IDL_tree p = IDL_node_new (IDLN_WIDE_CHAR);

	IDL_WIDE_CHAR (p).value = value;

	return p;
}

IDL_tree IDL_fixed_new (char *value)
{
	IDL_tree p = IDL_node_new (IDLN_FIXED);

	IDL_FIXED (p).value = value;

	return p;
}

IDL_tree IDL_float_new (double value)
{
	IDL_tree p = IDL_node_new (IDLN_FLOAT);

	IDL_FLOAT (p).value = value;

	return p;
}

IDL_tree IDL_boolean_new (unsigned value)
{
	IDL_tree p = IDL_node_new (IDLN_BOOLEAN);

	IDL_BOOLEAN (p).value = value;

	return p;
}

IDL_tree IDL_ident_new (char *str)
{
	IDL_tree p = IDL_node_new (IDLN_IDENT);

	IDL_IDENT (p).str = str;

	return p;
}

IDL_tree IDL_member_new (IDL_tree type_spec, IDL_tree dcls)
{
	IDL_tree p = IDL_node_new (IDLN_MEMBER);

	__IDL_assign_up_node (p, type_spec);
	__IDL_assign_up_node (p, dcls);
	IDL_MEMBER (p).type_spec = type_spec;
	IDL_MEMBER (p).dcls = dcls;

	return p;
}

IDL_tree IDL_native_new (IDL_tree ident)
{
	IDL_tree p = IDL_node_new (IDLN_NATIVE);

	__IDL_assign_up_node (p, ident);
	__IDL_assign_location (p, ident);
	IDL_NATIVE (p).ident = ident;

	return p;
}

IDL_tree IDL_type_dcl_new (IDL_tree type_spec, IDL_tree dcls)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_DCL);

	__IDL_assign_up_node (p, type_spec);
	__IDL_assign_up_node (p, dcls);
	__IDL_assign_location (p, IDL_LIST (dcls).data);
	IDL_TYPE_DCL (p).type_spec = type_spec;
	IDL_TYPE_DCL (p).dcls = dcls;

	return p;
}

IDL_tree IDL_type_float_new (enum IDL_float_type f_type)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_FLOAT);

	IDL_TYPE_FLOAT (p).f_type = f_type;

	return p;
}

IDL_tree IDL_type_fixed_new (IDL_tree positive_int_const,
			     IDL_tree integer_lit)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_FIXED);

	__IDL_assign_up_node (p, positive_int_const);
	__IDL_assign_up_node (p, integer_lit);
	IDL_TYPE_FIXED (p).positive_int_const = positive_int_const;
	IDL_TYPE_FIXED (p).integer_lit = integer_lit;

	return p;
}

IDL_tree IDL_type_integer_new (unsigned f_signed, enum IDL_integer_type f_type)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_INTEGER);

	IDL_TYPE_INTEGER (p).f_signed = f_signed;
	IDL_TYPE_INTEGER (p).f_type = f_type;

	return p;
}

IDL_tree IDL_type_char_new (void)
{
	return IDL_node_new (IDLN_TYPE_CHAR);
}

IDL_tree IDL_type_wide_char_new (void)
{
	return IDL_node_new (IDLN_TYPE_WIDE_CHAR);
}

IDL_tree IDL_type_boolean_new (void)
{
	return IDL_node_new (IDLN_TYPE_BOOLEAN);
}

IDL_tree IDL_type_octet_new (void)
{
	return IDL_node_new (IDLN_TYPE_OCTET);
}

IDL_tree IDL_type_any_new (void)
{
	return IDL_node_new (IDLN_TYPE_ANY);
}

IDL_tree IDL_type_object_new (void)
{
	return IDL_node_new (IDLN_TYPE_OBJECT);
}

IDL_tree IDL_type_typecode_new (void)
{
	return IDL_node_new (IDLN_TYPE_TYPECODE);
}

IDL_tree IDL_type_string_new (IDL_tree positive_int_const)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_STRING);

	__IDL_assign_up_node (p, positive_int_const);
	IDL_TYPE_STRING (p).positive_int_const = positive_int_const;

	return p;
}

IDL_tree IDL_type_wide_string_new (IDL_tree positive_int_const)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_WIDE_STRING);

	__IDL_assign_up_node (p, positive_int_const);
	IDL_TYPE_WIDE_STRING (p).positive_int_const = positive_int_const;

	return p;
}

IDL_tree IDL_type_array_new (IDL_tree ident,
			     IDL_tree size_list)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_ARRAY);

	__IDL_assign_up_node (p, ident);
	__IDL_assign_up_node (p, size_list);
	__IDL_assign_location (p, ident);
	IDL_TYPE_ARRAY (p).ident = ident;
	IDL_TYPE_ARRAY (p).size_list = size_list;

	return p;
}

IDL_tree IDL_type_sequence_new (IDL_tree simple_type_spec,
				IDL_tree positive_int_const)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_SEQUENCE);

	__IDL_assign_up_node (p, simple_type_spec);
	__IDL_assign_up_node (p, positive_int_const);
	IDL_TYPE_SEQUENCE (p).simple_type_spec = simple_type_spec;
	IDL_TYPE_SEQUENCE (p).positive_int_const = positive_int_const;

	return p;
}

IDL_tree IDL_type_struct_new (IDL_tree ident, IDL_tree member_list)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_STRUCT);

	__IDL_assign_up_node (p, ident);
	__IDL_assign_up_node (p, member_list);
	__IDL_assign_location (p, ident);
	IDL_TYPE_STRUCT (p).ident = ident;
	IDL_TYPE_STRUCT (p).member_list = member_list;

	return p;
}

IDL_tree IDL_type_union_new (IDL_tree ident, IDL_tree switch_type_spec, IDL_tree switch_body)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_UNION);

	__IDL_assign_up_node (p, ident);
	__IDL_assign_up_node (p, switch_type_spec);
	__IDL_assign_up_node (p, switch_body);
	__IDL_assign_location (p, ident);
	IDL_TYPE_UNION (p).ident = ident;
	IDL_TYPE_UNION (p).switch_type_spec = switch_type_spec;
	IDL_TYPE_UNION (p).switch_body = switch_body;

	return p;
}

IDL_tree IDL_type_enum_new (IDL_tree ident, IDL_tree enumerator_list)
{
	IDL_tree p = IDL_node_new (IDLN_TYPE_ENUM);

	__IDL_assign_up_node (p, ident);
	__IDL_assign_up_node (p, enumerator_list);
	__IDL_assign_location (p, ident);
	IDL_TYPE_ENUM (p).ident = ident;
	IDL_TYPE_ENUM (p).enumerator_list = enumerator_list;

	return p;
}

IDL_tree IDL_case_stmt_new (IDL_tree labels, IDL_tree element_spec)
{
	IDL_tree p = IDL_node_new (IDLN_CASE_STMT);

	__IDL_assign_up_node (p, labels);
	__IDL_assign_up_node (p, element_spec);
	IDL_CASE_STMT (p).labels = labels;
	IDL_CASE_STMT (p).element_spec = element_spec;

	return p;
}

IDL_tree IDL_interface_new (IDL_tree ident, IDL_tree inheritance_spec, IDL_tree body)
{
	IDL_tree p = IDL_node_new (IDLN_INTERFACE);

	/* Make sure the up node points to the interface */
	if (ident && IDL_NODE_UP (ident) &&
	    IDL_NODE_TYPE (IDL_NODE_UP (ident)) != IDLN_INTERFACE)
		IDL_NODE_UP (ident) = NULL;
	__IDL_assign_up_node (p, ident);
	__IDL_assign_up_node (p, inheritance_spec);
	__IDL_assign_up_node (p, body);
	IDL_INTERFACE (p).ident = ident;
	IDL_INTERFACE (p).inheritance_spec = inheritance_spec;
	IDL_INTERFACE (p).body = body;

	return p;
}

IDL_tree IDL_module_new (IDL_tree ident, IDL_tree definition_list)
{
	IDL_tree p = IDL_node_new (IDLN_MODULE);

	__IDL_assign_up_node (p, ident);
	__IDL_assign_up_node (p, definition_list);
	__IDL_assign_location (p, ident);
	IDL_MODULE (p).ident = ident;
	IDL_MODULE (p).definition_list = definition_list;

	return p;
}

IDL_tree IDL_binop_new (enum IDL_binop op, IDL_tree left, IDL_tree right)
{
	IDL_tree p = IDL_node_new (IDLN_BINOP);

	__IDL_assign_up_node (p, left);
	__IDL_assign_up_node (p, right);
	IDL_BINOP (p).op = op;
	IDL_BINOP (p).left = left;
	IDL_BINOP (p).right = right;

	return p;
}

IDL_tree IDL_unaryop_new (enum IDL_unaryop op, IDL_tree operand)
{
	IDL_tree p = IDL_node_new (IDLN_UNARYOP);

	__IDL_assign_up_node (p, operand);
	IDL_UNARYOP (p).op = op;
	IDL_UNARYOP (p).operand = operand;

	return p;
}

IDL_tree IDL_codefrag_new (char *desc, GSList *lines)
{
	IDL_tree p = IDL_node_new (IDLN_CODEFRAG);

	IDL_CODEFRAG (p).desc = desc;
	IDL_CODEFRAG (p).lines = lines;

	return p;
}

IDL_tree IDL_srcfile_new (char *filename, int seenCnt, gboolean isTop, gboolean wasInhibit)
{
	IDL_tree p = IDL_node_new (IDLN_SRCFILE);
	IDL_SRCFILE (p).filename = filename;
	IDL_SRCFILE (p).seenCnt = seenCnt;
	IDL_SRCFILE (p).isTop = isTop;
	IDL_SRCFILE (p).wasInhibit = wasInhibit;

	return p;
}

IDL_tree IDL_const_dcl_new (IDL_tree const_type, IDL_tree ident, IDL_tree const_exp)
{
	IDL_tree p = IDL_node_new (IDLN_CONST_DCL);

	__IDL_assign_up_node (p, const_type);
	__IDL_assign_up_node (p, ident);
	__IDL_assign_up_node (p, const_exp);
	__IDL_assign_location (p, ident);
	IDL_CONST_DCL (p).const_type = const_type;
	IDL_CONST_DCL (p).ident = ident;
	IDL_CONST_DCL (p).const_exp = const_exp;

	return p;
}

IDL_tree IDL_except_dcl_new (IDL_tree ident, IDL_tree members)
{
	IDL_tree p = IDL_node_new (IDLN_EXCEPT_DCL);

	__IDL_assign_up_node (p, ident);
	__IDL_assign_up_node (p, members);
	__IDL_assign_location (p, ident);
	IDL_EXCEPT_DCL (p).ident = ident;
	IDL_EXCEPT_DCL (p).members = members;

	return p;
}

IDL_tree IDL_attr_dcl_new (unsigned f_readonly,
			   IDL_tree param_type_spec,
			   IDL_tree simple_declarations)
{
	IDL_tree p = IDL_node_new (IDLN_ATTR_DCL);

	__IDL_assign_up_node (p, param_type_spec);
	__IDL_assign_up_node (p, simple_declarations);
	__IDL_assign_location (p, IDL_LIST (simple_declarations).data);
	IDL_ATTR_DCL (p).f_readonly = f_readonly;
	IDL_ATTR_DCL (p).param_type_spec = param_type_spec;
	IDL_ATTR_DCL (p).simple_declarations = simple_declarations;

	return p;
}

IDL_tree IDL_op_dcl_new (unsigned f_oneway,
			 IDL_tree op_type_spec,
			 IDL_tree ident,
			 IDL_tree parameter_dcls,
			 IDL_tree raises_expr,
			 IDL_tree context_expr)
{
	IDL_tree p = IDL_node_new (IDLN_OP_DCL);

	__IDL_assign_up_node (p, op_type_spec);
	__IDL_assign_up_node (p, ident);
	__IDL_assign_up_node (p, parameter_dcls);
	__IDL_assign_up_node (p, raises_expr);
	__IDL_assign_up_node (p, context_expr);
	__IDL_assign_location (p, ident);
	IDL_OP_DCL (p).f_oneway = f_oneway;
	IDL_OP_DCL (p).op_type_spec = op_type_spec;
	IDL_OP_DCL (p).ident = ident;
	IDL_OP_DCL (p).parameter_dcls = parameter_dcls;
	IDL_OP_DCL (p).raises_expr = raises_expr;
	IDL_OP_DCL (p).context_expr = context_expr;

	return p;
}

IDL_tree IDL_param_dcl_new (enum IDL_param_attr attr,
			    IDL_tree param_type_spec,
			    IDL_tree simple_declarator)
{
	IDL_tree p = IDL_node_new (IDLN_PARAM_DCL);

	__IDL_assign_up_node (p, param_type_spec);
	__IDL_assign_up_node (p, simple_declarator);
	__IDL_assign_location (p, simple_declarator);
	IDL_PARAM_DCL (p).attr = attr;
	IDL_PARAM_DCL (p).param_type_spec = param_type_spec;
	IDL_PARAM_DCL (p).simple_declarator = simple_declarator;

	return p;
}

IDL_tree IDL_forward_dcl_new (IDL_tree ident)
{
	IDL_tree p = IDL_node_new (IDLN_FORWARD_DCL);

	__IDL_assign_up_node (p, ident);
	__IDL_assign_location (p, ident);
	IDL_FORWARD_DCL (p).ident = ident;

	return p;
}

IDL_tree IDL_check_type_cast (const IDL_tree tree, IDL_tree_type type,
			      const char *file, int line, const char *function)
{
	if (__IDL_check_type_casts) {
		if (tree == NULL) {
			g_warning ("file %s: line %d: (%s) invalid type cast attempt,"
				   " NULL tree to %s\n",
				   file, line, function,
				   IDL_tree_type_names[type]);
		}
		else if (IDL_NODE_TYPE (tree) != type) {
			g_warning ("file %s: line %d: (%s) expected IDL tree type %s,"
				   " but got %s\n",
				   file, line, function,
				   IDL_tree_type_names[type], IDL_NODE_TYPE_NAME (tree));

		}
	}
	return tree;
}

IDL_tree IDL_gentree_chain_sibling (IDL_tree from, IDL_tree data)
{
	IDL_tree p;

	if (from == NULL)
		return NULL;

	p = IDL_gentree_new_sibling (from, data);
	IDL_NODE_UP (p) = IDL_NODE_UP (from);

	return p;
}

IDL_tree IDL_gentree_chain_child (IDL_tree from, IDL_tree data)
{
	IDL_tree p;

	if (from == NULL)
		return NULL;

	p = IDL_gentree_new (IDL_GENTREE (from).hash_func,
			     IDL_GENTREE (from).key_compare_func,
			     data);
	IDL_NODE_UP (p) = from;

	g_hash_table_insert (IDL_GENTREE (from).children, data, p);

	return p;
}

IDL_tree IDL_get_parent_node (IDL_tree p, IDL_tree_type type, int *levels)
{
	int count = 0;

	if (p == NULL)
		return NULL;

	if (type == IDLN_ANY)
		return IDL_NODE_UP (p);

	while (p != NULL && IDL_NODE_TYPE (p) != type) {

		if (IDL_NODE_IS_SCOPED (p))
			++count;

		p = IDL_NODE_UP (p);
	}

	if (p != NULL)
		if (levels != NULL)
			*levels = count;

	return p;
}

IDL_tree IDL_tree_get_scope (IDL_tree p)
{
	g_return_val_if_fail (p != NULL, NULL);

	if (IDL_NODE_TYPE (p) == IDLN_GENTREE)
		return p;

	if (!IDL_NODE_IS_SCOPED (p)) {
		g_warning ("Node type %s isn't scoped", IDL_NODE_TYPE_NAME (p));
		return NULL;
	}

	switch (IDL_NODE_TYPE (p)) {
	case IDLN_IDENT:
		return IDL_IDENT_TO_NS (p);

	case IDLN_INTERFACE:
		return IDL_IDENT_TO_NS (IDL_INTERFACE (p).ident);

	case IDLN_MODULE:
		return IDL_IDENT_TO_NS (IDL_MODULE (p).ident);

	case IDLN_EXCEPT_DCL:
		return IDL_IDENT_TO_NS (IDL_EXCEPT_DCL (p).ident);

	case IDLN_OP_DCL:
		return IDL_IDENT_TO_NS (IDL_OP_DCL (p).ident);

	case IDLN_TYPE_ENUM:
		return IDL_IDENT_TO_NS (IDL_TYPE_ENUM (p).ident);

	case IDLN_TYPE_STRUCT:
		return IDL_IDENT_TO_NS (IDL_TYPE_STRUCT (p).ident);

	case IDLN_TYPE_UNION:
		return IDL_IDENT_TO_NS (IDL_TYPE_UNION (p).ident);

	default:
		return NULL;
	}
}

typedef struct {
	IDL_tree_func pre_tree_func;
	IDL_tree_func post_tree_func;
	gpointer user_data;
} IDLTreeWalkRealData;

static void IDL_tree_walk_real (IDL_tree_func_data *tfd, IDLTreeWalkRealData *data)
{
	IDL_tree_func_data down_tfd;
	gboolean recurse = TRUE;
	IDL_tree p, q;

	if (tfd->tree == NULL)
		return;

	tfd->state->bottom = tfd;
	tfd->step = 0;
	tfd->data = NULL;

	if (data->pre_tree_func)
		recurse = (*data->pre_tree_func) (tfd, data->user_data);
	++tfd->step;

	down_tfd.state = tfd->state;
	down_tfd.up = tfd;
	down_tfd.level = tfd->level + 1;

	p = tfd->tree;

	if (recurse) switch (IDL_NODE_TYPE (p)) {
	case IDLN_INTEGER:
	case IDLN_STRING:
	case IDLN_CHAR:
	case IDLN_FIXED:
	case IDLN_FLOAT:
	case IDLN_BOOLEAN:
	case IDLN_IDENT:
	case IDLN_TYPE_WIDE_CHAR:
	case IDLN_TYPE_BOOLEAN:
	case IDLN_TYPE_OCTET:
	case IDLN_TYPE_ANY:
	case IDLN_TYPE_OBJECT:
	case IDLN_TYPE_TYPECODE:
	case IDLN_TYPE_FLOAT:
	case IDLN_TYPE_INTEGER:
	case IDLN_TYPE_CHAR:
	case IDLN_CODEFRAG:
	case IDLN_SRCFILE:
		break;

	case IDLN_LIST:
		for (q = p; q; q = IDL_LIST (q).next) {
			down_tfd.tree = IDL_LIST (q).data;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_GENTREE:
		g_error ("IDLN_GENTREE walk not implemented!");
		break;

	case IDLN_MEMBER:
		down_tfd.tree = IDL_MEMBER (p).type_spec;
		IDL_tree_walk_real (&down_tfd, data);
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_MEMBER (p).dcls;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_NATIVE:
		down_tfd.tree = IDL_NATIVE (p).ident;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_TYPE_DCL:
		down_tfd.tree = IDL_TYPE_DCL (p).type_spec;
		IDL_tree_walk_real (&down_tfd, data);
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_TYPE_DCL (p).dcls;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_CONST_DCL:
		down_tfd.tree = IDL_CONST_DCL (p).const_type;
		IDL_tree_walk_real (&down_tfd, data);
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_CONST_DCL (p).ident;
			IDL_tree_walk_real (&down_tfd, data);
			down_tfd.tree = IDL_CONST_DCL (p).const_exp;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_EXCEPT_DCL:
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_EXCEPT_DCL (p).ident;
			IDL_tree_walk_real (&down_tfd, data);
		}
		down_tfd.tree = IDL_EXCEPT_DCL (p).members;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_ATTR_DCL:
		down_tfd.tree = IDL_ATTR_DCL (p).param_type_spec;
		IDL_tree_walk_real (&down_tfd, data);
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_ATTR_DCL (p).simple_declarations;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_OP_DCL:
		down_tfd.tree = IDL_OP_DCL (p).op_type_spec;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_OP_DCL (p).ident;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_OP_DCL (p).parameter_dcls;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_OP_DCL (p).raises_expr;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_OP_DCL (p).context_expr;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_PARAM_DCL:
		down_tfd.tree = IDL_PARAM_DCL (p).param_type_spec;
		IDL_tree_walk_real (&down_tfd, data);
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_PARAM_DCL (p).simple_declarator;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_FORWARD_DCL:
		down_tfd.tree = IDL_FORWARD_DCL (p).ident;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_TYPE_FIXED:
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_TYPE_FIXED (p).positive_int_const;
			IDL_tree_walk_real (&down_tfd, data);
			down_tfd.tree = IDL_TYPE_FIXED (p).integer_lit;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_TYPE_STRING:
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_TYPE_STRING (p).positive_int_const;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_TYPE_WIDE_STRING:
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_TYPE_WIDE_STRING (p).positive_int_const;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_TYPE_ENUM:
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_TYPE_ENUM (p).ident;
			IDL_tree_walk_real (&down_tfd, data);
			down_tfd.tree = IDL_TYPE_ENUM (p).enumerator_list;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_TYPE_SEQUENCE:
		down_tfd.tree = IDL_TYPE_SEQUENCE (p).simple_type_spec;
		IDL_tree_walk_real (&down_tfd, data);
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_TYPE_SEQUENCE (p).positive_int_const;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_TYPE_ARRAY:
		down_tfd.tree = IDL_TYPE_ARRAY (p).ident;
		IDL_tree_walk_real (&down_tfd, data);
		if ( (tfd->state->flags & IDL_WalkF_TypespecOnly)==0 ) {
			down_tfd.tree = IDL_TYPE_ARRAY (p).size_list;
			IDL_tree_walk_real (&down_tfd, data);
		}
		break;

	case IDLN_TYPE_STRUCT:
		down_tfd.tree = IDL_TYPE_STRUCT (p).ident;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_TYPE_STRUCT (p).member_list;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_TYPE_UNION:
		down_tfd.tree = IDL_TYPE_UNION (p).ident;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_TYPE_UNION (p).switch_type_spec;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_TYPE_UNION (p).switch_body;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_CASE_STMT:
		down_tfd.tree = IDL_CASE_STMT (p).labels;
		IDL_tree_walk_real (&down_tfd, data);
		/* FIXME */
		down_tfd.tree = IDL_CASE_STMT (p).element_spec;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_INTERFACE:
		down_tfd.tree = IDL_INTERFACE (p).ident;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_INTERFACE (p).inheritance_spec;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_INTERFACE (p).body;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_MODULE:
		down_tfd.tree = IDL_MODULE (p).ident;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_MODULE (p).definition_list;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_BINOP:
		down_tfd.tree = IDL_BINOP (p).left;
		IDL_tree_walk_real (&down_tfd, data);
		down_tfd.tree = IDL_BINOP (p).right;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	case IDLN_UNARYOP:
		down_tfd.tree = IDL_UNARYOP (p).operand;
		IDL_tree_walk_real (&down_tfd, data);
		break;

	default:
		g_warning ("IDL_tree_walk_real: unknown node type %s\n",
			   IDL_NODE_TYPE_NAME (p));
		break;
	}

	if (data->post_tree_func)
			(void) (*data->post_tree_func) (tfd, data->user_data);

	tfd->state->bottom = tfd->up;
}

void IDL_tree_walk2 (IDL_tree p, IDL_tree_func_data *current,
		    glong flags, 
		    IDL_tree_func pre_tree_func, IDL_tree_func post_tree_func,
		    gpointer user_data)
{
	IDLTreeWalkRealData data;
	IDL_tree_func_state tfs;
	IDL_tree_func_data tfd;

	g_return_if_fail (!(pre_tree_func == NULL && post_tree_func == NULL));

	data.pre_tree_func = pre_tree_func;
	data.post_tree_func = post_tree_func;
	data.user_data = user_data;

	tfs.up = current ? current->state : NULL;
	tfs.start = p;
	tfs.flags = flags;

	tfd.level = 0;
	if (current) {
	    tfd = *current;
	    tfd.level = ((tfd.level / 1000)+1) * 1000;
	}
	tfd.state = &tfs;
	tfd.up = current;
	tfd.tree = p;

	IDL_tree_walk_real (&tfd, &data);
}

void IDL_tree_walk (IDL_tree p, IDL_tree_func_data *current,
		    IDL_tree_func pre_tree_func, IDL_tree_func post_tree_func,
		    gpointer user_data)
{
    IDL_tree_walk2 (p, current, /*flags*/0, 
      pre_tree_func, post_tree_func, user_data);
}

void IDL_tree_walk_in_order (IDL_tree p, IDL_tree_func tree_func, gpointer user_data)
{
	IDL_tree_walk2 (p, NULL, /*flags*/0, tree_func, NULL, user_data);
}

static void __IDL_tree_free (IDL_tree p);

static int tree_free_but_this (IDL_tree data, IDL_tree p, IDL_tree this_one)
{
	if (p == this_one)
		return TRUE;

	__IDL_tree_free (p);

	return TRUE;
}

static void property_free (char *key, char *value)
{
	g_free (key);
	g_free (value);
}

void __IDL_free_properties (GHashTable *table)
{
	if (table) {
		g_hash_table_foreach (table, (GHFunc) property_free, NULL);
		g_hash_table_destroy (table);
	}
}

/* Free associated node data, regardless of refcounts */
static void IDL_tree_free_real (IDL_tree p)
{
	GSList *slist;

	assert (p != NULL);

	switch (IDL_NODE_TYPE (p)) {
	case IDLN_GENTREE:
		g_hash_table_foreach (IDL_GENTREE (p).children,
				      (GHFunc) tree_free_but_this, NULL);
		g_hash_table_destroy (IDL_GENTREE (p).children);
		break;

	case IDLN_FIXED:
		g_free (IDL_FIXED (p).value);
		break;

	case IDLN_STRING:
		g_free (IDL_STRING (p).value);
		break;

	case IDLN_WIDE_STRING:
		g_free (IDL_WIDE_STRING (p).value);
		break;

	case IDLN_CHAR:
		g_free (IDL_CHAR (p).value);
		break;

	case IDLN_WIDE_CHAR:
		g_free (IDL_WIDE_CHAR (p).value);
		break;

	case IDLN_IDENT:
		g_free (IDL_IDENT (p).str);
		g_free (IDL_IDENT_REPO_ID (p));
		for (slist = IDL_IDENT (p).comments; slist; slist = slist->next)
			g_free (slist->data);
		g_slist_free (IDL_IDENT (p).comments);
		break;

	case IDLN_NATIVE:
		g_free (IDL_NATIVE (p).user_type);
		break;

	case IDLN_INTERFACE:
		break;

	case IDLN_CODEFRAG:
		g_free (IDL_CODEFRAG (p).desc);
		for (slist = IDL_CODEFRAG (p).lines; slist; slist = slist->next)
			g_free (slist->data);
		g_slist_free (IDL_CODEFRAG (p).lines);
		break;

	default:
		break;
	}

	__IDL_free_properties (IDL_NODE_PROPERTIES (p));

	g_free (p);
}

/* Free node taking into account refcounts */
static void __IDL_tree_free (IDL_tree p)
{
	if (p == NULL)
		return;

	if (--IDL_NODE_REFS (p) <= 0)
		IDL_tree_free_real (p);
}

/* Free a set of references of an entire tree */
void IDL_tree_free (IDL_tree p)
{
	IDL_tree q;

	if (p == NULL)
		return;

	switch (IDL_NODE_TYPE (p)) {
	case IDLN_INTEGER:
	case IDLN_FLOAT:
	case IDLN_BOOLEAN:
	case IDLN_TYPE_FLOAT:
	case IDLN_TYPE_INTEGER:
	case IDLN_TYPE_CHAR:
	case IDLN_TYPE_WIDE_CHAR:
	case IDLN_TYPE_BOOLEAN:
	case IDLN_TYPE_OCTET:
	case IDLN_TYPE_ANY:
	case IDLN_TYPE_OBJECT:
	case IDLN_TYPE_TYPECODE:
	case IDLN_FIXED:
	case IDLN_STRING:
	case IDLN_WIDE_STRING:
	case IDLN_CHAR:
	case IDLN_WIDE_CHAR:
	case IDLN_IDENT:
	case IDLN_CODEFRAG:
	case IDLN_SRCFILE:
		__IDL_tree_free (p);
		break;

	case IDLN_LIST:
		while (p) {
			IDL_tree_free (IDL_LIST (p).data);
			q = IDL_LIST (p).next;
			__IDL_tree_free (p);
			p = q;
		}
		break;

	case IDLN_GENTREE:
		g_hash_table_foreach (IDL_GENTREE (p).siblings,
				      (GHFunc) tree_free_but_this, p);
		g_hash_table_destroy (IDL_GENTREE (p).siblings);
		__IDL_tree_free (p);
		break;

	case IDLN_MEMBER:
		IDL_tree_free (IDL_MEMBER (p).type_spec);
		IDL_tree_free (IDL_MEMBER (p).dcls);
		__IDL_tree_free (p);
		break;

	case IDLN_NATIVE:
		IDL_tree_free (IDL_NATIVE (p).ident);
		__IDL_tree_free (p);
		break;

	case IDLN_TYPE_ENUM:
		IDL_tree_free (IDL_TYPE_ENUM (p).ident);
		IDL_tree_free (IDL_TYPE_ENUM (p).enumerator_list);
		__IDL_tree_free (p);
		break;

	case IDLN_TYPE_SEQUENCE:
		IDL_tree_free (IDL_TYPE_SEQUENCE (p).simple_type_spec);
		IDL_tree_free (IDL_TYPE_SEQUENCE (p).positive_int_const);
		__IDL_tree_free (p);
		break;

	case IDLN_TYPE_ARRAY:
		IDL_tree_free (IDL_TYPE_ARRAY (p).ident);
		IDL_tree_free (IDL_TYPE_ARRAY (p).size_list);
		__IDL_tree_free (p);
		break;

	case IDLN_TYPE_STRUCT:
		IDL_tree_free (IDL_TYPE_STRUCT (p).ident);
		IDL_tree_free (IDL_TYPE_STRUCT (p).member_list);
		__IDL_tree_free (p);
		break;

	case IDLN_TYPE_UNION:
		IDL_tree_free (IDL_TYPE_UNION (p).ident);
		IDL_tree_free (IDL_TYPE_UNION (p).switch_type_spec);
		IDL_tree_free (IDL_TYPE_UNION (p).switch_body);
		__IDL_tree_free (p);
		break;

	case IDLN_TYPE_DCL:
		IDL_tree_free (IDL_TYPE_DCL (p).type_spec);
		IDL_tree_free (IDL_TYPE_DCL (p).dcls);
		__IDL_tree_free (p);
		break;

	case IDLN_CONST_DCL:
		IDL_tree_free (IDL_CONST_DCL (p).const_type);
		IDL_tree_free (IDL_CONST_DCL (p).ident);
		IDL_tree_free (IDL_CONST_DCL (p).const_exp);
		__IDL_tree_free (p);
		break;

	case IDLN_EXCEPT_DCL:
		IDL_tree_free (IDL_EXCEPT_DCL (p).ident);
		IDL_tree_free (IDL_EXCEPT_DCL (p).members);
		__IDL_tree_free (p);
		break;

	case IDLN_ATTR_DCL:
		IDL_tree_free (IDL_ATTR_DCL (p).param_type_spec);
		IDL_tree_free (IDL_ATTR_DCL (p).simple_declarations);
		__IDL_tree_free (p);
		break;

	case IDLN_OP_DCL:
		IDL_tree_free (IDL_OP_DCL (p).op_type_spec);
		IDL_tree_free (IDL_OP_DCL (p).ident);
		IDL_tree_free (IDL_OP_DCL (p).parameter_dcls);
		IDL_tree_free (IDL_OP_DCL (p).raises_expr);
		IDL_tree_free (IDL_OP_DCL (p).context_expr);
		__IDL_tree_free (p);
		break;

	case IDLN_PARAM_DCL:
		IDL_tree_free (IDL_PARAM_DCL (p).param_type_spec);
		IDL_tree_free (IDL_PARAM_DCL (p).simple_declarator);
		__IDL_tree_free (p);
		break;

	case IDLN_FORWARD_DCL:
		IDL_tree_free (IDL_FORWARD_DCL (p).ident);
		__IDL_tree_free (p);
		break;

	case IDLN_TYPE_STRING:
		IDL_tree_free (IDL_TYPE_STRING (p).positive_int_const);
		__IDL_tree_free (p);
		break;

	case IDLN_TYPE_WIDE_STRING:
		IDL_tree_free (IDL_TYPE_WIDE_STRING (p).positive_int_const);
		__IDL_tree_free (p);
		break;

	case IDLN_TYPE_FIXED:
		IDL_tree_free (IDL_TYPE_FIXED (p).positive_int_const);
		IDL_tree_free (IDL_TYPE_FIXED (p).integer_lit);
		__IDL_tree_free (p);
		break;

	case IDLN_CASE_STMT:
		IDL_tree_free (IDL_CASE_STMT (p).labels);
		IDL_tree_free (IDL_CASE_STMT (p).element_spec);
		__IDL_tree_free (p);
		break;

	case IDLN_INTERFACE:
		IDL_tree_free (IDL_INTERFACE (p).ident);
		IDL_tree_free (IDL_INTERFACE (p).inheritance_spec);
		IDL_tree_free (IDL_INTERFACE (p).body);
		__IDL_tree_free (p);
		break;

	case IDLN_MODULE:
		IDL_tree_free (IDL_MODULE (p).ident);
		IDL_tree_free (IDL_MODULE (p).definition_list);
		__IDL_tree_free (p);
		break;

	case IDLN_BINOP:
		IDL_tree_free (IDL_BINOP (p).left);
		IDL_tree_free (IDL_BINOP (p).right);
		__IDL_tree_free (p);
		break;

	case IDLN_UNARYOP:
		IDL_tree_free (IDL_UNARYOP (p).operand);
		__IDL_tree_free (p);
		break;

	default:
		g_warning ("Free unknown node: %d\n", IDL_NODE_TYPE (p));
		break;
	}
}

#define C_ESC(a,b)				case a: *p++ = b; ++s; break
gchar *IDL_do_escapes (const char *s)
{
	char *p, *q;

	if (!s)
		return NULL;

	p = q = g_malloc (strlen (s) + 1);

	while (*s) {
		if (*s != '\\') {
			*p++ = *s++;
			continue;
		}
		++s;
		if (*s == 'x') {
			char hex[3];
			int n;
			hex[0] = 0;
			++s;
			sscanf (s, "%2[0-9a-fA-F]", hex);
 			s += strlen (hex);
			sscanf (hex, "%x", &n);
			*p++ = n;
			continue;
		}
		if (*s >= '0' && *s <= '7') {
			char oct[4];
			int n;
			oct[0] = 0;
			sscanf (s, "%3[0-7]", oct);
 			s += strlen (oct);
			sscanf (oct, "%o", &n);
			*p++ = n;
			continue;
		}
		switch (*s) {
			C_ESC ('n','\n');
			C_ESC ('t','\t');
			C_ESC ('v','\v');
			C_ESC ('b','\b');
			C_ESC ('r','\r');
			C_ESC ('f','\f');
			C_ESC ('a','\a');
			C_ESC ('\\','\\');
			C_ESC ('?','?');
			C_ESC ('\'','\'');
			C_ESC ('"','"');
		}
	}
	*p = 0;

	return q;
}

int IDL_list_length (IDL_tree list)
{
	IDL_tree curitem;
	int length;

	for (curitem = list, length = 0; curitem;
	     curitem = IDL_LIST (curitem).next)
		length++;

	return length;
}

IDL_tree IDL_list_nth (IDL_tree list, int n)
{
	IDL_tree curitem;
	int i;

	for (curitem = list, i = 0; i < n && curitem;
	     curitem = IDL_LIST (curitem).next, i++) ;

	return curitem;
}

const char *IDL_tree_property_get (IDL_tree tree, const char *key)
{
	g_return_val_if_fail (tree != NULL, NULL);
	g_return_val_if_fail (key != NULL, NULL);

	if (!IDL_NODE_PROPERTIES (tree))
		return NULL;

	return g_hash_table_lookup (IDL_NODE_PROPERTIES (tree), key);
}

void IDL_tree_property_set (IDL_tree tree, const char *key, const char *value)
{
	g_return_if_fail (tree != NULL);
	g_return_if_fail (key != NULL);

	if (!IDL_NODE_PROPERTIES (tree))
		IDL_NODE_PROPERTIES (tree) = g_hash_table_new (
			IDL_strcase_hash, IDL_strcase_equal);
	else if (IDL_tree_property_get (tree, key))
		IDL_tree_property_remove (tree, key);

	g_hash_table_insert (IDL_NODE_PROPERTIES (tree), g_strdup (key), g_strdup (value));
}

gboolean IDL_tree_property_remove (IDL_tree tree, const char *key)
{
	gboolean removed = FALSE;
	char *val;

	g_return_val_if_fail (tree != NULL, FALSE);
	g_return_val_if_fail (key != NULL, FALSE);

	if (!IDL_NODE_PROPERTIES (tree))
		return FALSE;

	if ((val = g_hash_table_lookup (IDL_NODE_PROPERTIES (tree), key))) {
		g_hash_table_remove (IDL_NODE_PROPERTIES (tree), key);
		g_free (val);
		removed = TRUE;
	}

	return removed;
}

static void property_set (char *key, char *value, IDL_tree tree)
{
	IDL_tree_property_set (tree, key, value);
}

void IDL_tree_properties_copy (IDL_tree from_tree, IDL_tree to_tree)
{
	g_return_if_fail (from_tree != NULL);
	g_return_if_fail (to_tree != NULL);

	if (IDL_NODE_PROPERTIES (from_tree))
		g_hash_table_foreach (IDL_NODE_PROPERTIES (from_tree),
				      (GHFunc) property_set, to_tree);
}

typedef struct {
	IDL_tree *root;
	GHashTable *removed_nodes;
} RemoveListNodeData;

static int remove_list_node (IDL_tree p, IDL_tree *list_head, RemoveListNodeData *data)
{
	assert (p != NULL);
	assert (IDL_NODE_TYPE (p) == IDLN_LIST);

	if (list_head)
		*list_head = IDL_list_remove (*list_head, p);
	else
		*data->root = IDL_list_remove (*data->root, p);

	if (data->removed_nodes) {
		if (!g_hash_table_lookup_extended (data->removed_nodes, p, NULL, NULL))
			g_hash_table_insert (data->removed_nodes, p, p);
		/*
		  We shouldn't need this since we have removed it from the tree,
		  but we might need it for multiple declspec (inhibits) in the same
		  subtree.
		  IDL_tree_walk_in_order (p, (IDL_tree_func) inc_node_ref, NULL);
		*/
	} else
		IDL_tree_free (p);

	return TRUE;
}

/* Forward Declaration Resolution */
static int load_forward_dcls (IDL_tree_func_data *tfd, GHashTable *table)
{
	if (IDL_NODE_TYPE (tfd->tree) == IDLN_FORWARD_DCL) {
		char *s = IDL_ns_ident_to_qstring (IDL_FORWARD_DCL (tfd->tree).ident, "::", 0);

		if (!g_hash_table_lookup_extended (table, s, NULL, NULL))
			g_hash_table_insert (table, s, tfd->tree);
		else
			g_free (s);
	}

	return TRUE;
}

static int resolve_forward_dcls (IDL_tree_func_data *tfd, GHashTable *table)
{
	if (IDL_NODE_TYPE (tfd->tree) == IDLN_INTERFACE) {
		char *orig, *s = IDL_ns_ident_to_qstring (IDL_INTERFACE (tfd->tree).ident, "::", 0);

		if (g_hash_table_lookup_extended (table, s, (gpointer)&orig, NULL)) {
			g_hash_table_remove (table, orig);
			g_free (orig);
		}
		g_free (s);
	}

	return TRUE;
}

static int print_unresolved_forward_dcls (char *s, IDL_tree p)
{
	if (__IDL_flags & IDLF_PEDANTIC)
		IDL_tree_error (p, "Unresolved forward declaration `%s'", s);
	else
		IDL_tree_warning (p,
			IDL_WARNING1, "Unresolved forward declaration `%s'", s);
	g_free (s);

	return TRUE;
}

void IDL_tree_process_forward_dcls (IDL_tree *p, IDL_ns ns)
{
	GHashTable *table = g_hash_table_new (IDL_strcase_hash, IDL_strcase_equal);
	gint total, resolved;

	IDL_tree_walk_in_order (*p, (IDL_tree_func) load_forward_dcls, table);
	total = g_hash_table_size (table);
	IDL_tree_walk_in_order (*p, (IDL_tree_func) resolve_forward_dcls, table);
	resolved = total - g_hash_table_size (table);
	g_hash_table_foreach (table, (GHFunc) print_unresolved_forward_dcls, NULL);
	g_hash_table_destroy (table);
	if (__IDL_flags & IDLF_VERBOSE)
		g_message ("Forward declarations resolved: %d of %d", resolved, total);
}

/* Inhibit Creation Removal */
static int load_inhibits (IDL_tree_func_data *tfd, GHashTable *table)
{
	IDL_tree p, q, *list_head;

	p = tfd->tree;

	if (p != NULL &&
	    IDL_NODE_UP (p) &&
	    IDL_NODE_TYPE (IDL_NODE_UP (p)) == IDLN_LIST &&
	    IDL_NODE_DECLSPEC (p) & IDLF_DECLSPEC_INHIBIT &&
	    !g_hash_table_lookup_extended (table, IDL_NODE_UP (p), NULL, NULL)) {
		list_head = NULL;
		q = IDL_NODE_UP (IDL_NODE_UP (p));
		if (q) {
			switch (IDL_NODE_TYPE (q)) {
			case IDLN_MODULE:
				list_head = &IDL_MODULE (q).definition_list;
				break;

			case IDLN_INTERFACE:
				list_head = &IDL_INTERFACE (q).body;
				break;

			default:
				g_warning ("Unhandled node %s in load_inhibits",
					   IDL_NODE_TYPE_NAME (q));
				break;
			}
		}
		g_hash_table_insert (table, IDL_NODE_UP (p), list_head);

		return FALSE;
	}

	return TRUE;
}

void IDL_tree_remove_inhibits (IDL_tree *tree, IDL_ns ns)
{
	RemoveListNodeData data;
	GHashTable *table = g_hash_table_new (g_direct_hash, g_direct_equal);
	gint removed;

	g_return_if_fail (tree != NULL);
	g_return_if_fail (ns != NULL);

	IDL_tree_walk_in_order (*tree, (IDL_tree_func) load_inhibits, table);
	removed = g_hash_table_size (table);
	data.root = tree;
	data.removed_nodes = IDL_NS (ns).inhibits;
	g_hash_table_foreach (table, (GHFunc) remove_list_node, &data);
	g_hash_table_destroy (table);
	if (__IDL_flags & IDLF_VERBOSE)
		g_message ("Inhibited nodes removed: %d", removed);
}

/* Multi-Pass Empty Module Removal */
static int load_empty_modules (IDL_tree_func_data *tfd, GHashTable *table)
{
	IDL_tree p, q, *list_head;

	p = tfd->tree;

	if (IDL_NODE_TYPE (p) == IDLN_MODULE &&
	    IDL_MODULE (p).definition_list == NULL &&
	    IDL_NODE_UP (p) &&
	    IDL_NODE_TYPE (IDL_NODE_UP (p)) == IDLN_LIST &&
	    !g_hash_table_lookup_extended (table, IDL_NODE_UP (p), NULL, NULL)) {

		list_head = NULL;
		q = IDL_NODE_UP (IDL_NODE_UP (p));
		if (q) {
			assert (IDL_NODE_TYPE (q) == IDLN_MODULE);
			list_head = &IDL_MODULE (q).definition_list;
		}
		g_hash_table_insert (table, IDL_NODE_UP (p), list_head);
	}

	return TRUE;
}

void IDL_tree_remove_empty_modules (IDL_tree *p, IDL_ns ns)
{
	RemoveListNodeData data;
	gboolean done = FALSE;
	gint removed = 0;

	data.root = p;
	data.removed_nodes = NULL;

	while (!done) {
		GHashTable *table = g_hash_table_new (g_direct_hash, g_direct_equal);
		IDL_tree_walk_in_order (*p, (IDL_tree_func) load_empty_modules, table);
		removed += g_hash_table_size (table);
		done = g_hash_table_size (table) == 0;
		g_hash_table_foreach (table, (GHFunc) remove_list_node, &data);
		g_hash_table_destroy (table);
	}
	if (__IDL_flags & IDLF_VERBOSE)
		g_message ("Empty modules removed: %d", removed);
}

/*
 * IDL_tree to IDL backend
 */

#define DELIM_COMMA		", "
#define DELIM_ARRAY		"]["
#define DELIM_SPACE		" "

#define indent()		++data->ilev
#define unindent()		--data->ilev
#define doindent()		do {					\
	int i;								\
	if (!(data->flags & IDLF_OUTPUT_NO_NEWLINES))			\
		for (i = 0; i < data->ilev; ++i) {			\
			switch (data->mode) {				\
			case OUTPUT_FILE:				\
				fputc ('\t', data->u.o);		\
				break;					\
									\
			case OUTPUT_STRING:				\
				g_string_append_c (data->u.s, '\t');	\
				break;					\
									\
			default:					\
				break;					\
			}						\
		}							\
	else if (data->ilev > 0)					\
		dataf (data, DELIM_SPACE);				\
} while (0)
#define nl()			do {				\
	if (!(data->flags & IDLF_OUTPUT_NO_NEWLINES)) {		\
		switch (data->mode) {				\
		case OUTPUT_FILE:				\
			fputc ('\n', data->u.o);		\
			break;					\
								\
		case OUTPUT_STRING:				\
			g_string_append_c (data->u.s, '\n');	\
			break;					\
								\
		default:					\
			break;					\
		}						\
	}							\
} while (0)
#define save_flag(flagbit,val)	do {				\
	tfd->data = GUINT_TO_POINTER (				\
 		GPOINTER_TO_UINT (tfd->data) |			\
 		(data->flagbit ? (1U << flagbit##bit) : 0));	\
	data->flagbit = val;					\
} while (0)
#define restore_flag(flagbit)	do {			\
	data->flagbit = (GPOINTER_TO_UINT (		\
		tfd->data) >> flagbit##bit) & 1U;	\
} while (0)

typedef struct {
	IDL_ns ns;
	enum {
		OUTPUT_FILE,
		OUTPUT_STRING
	} mode;
	union {
		FILE *o;
		GString *s;
	} u;
	int ilev;
	unsigned long flags;

#define identsbit		0
	guint idents : 1;

#define literalsbit		1
	guint literals : 1;

#define inline_propsbit		2
	guint inline_props : 1;

#define su_defbit		3
	guint su_def : 1;
} IDL_output_data;

static void dataf (IDL_output_data *data, const char *fmt, ...)
G_GNUC_PRINTF (2, 3);

static void idataf (IDL_output_data *data, const char *fmt, ...)
G_GNUC_PRINTF (2, 3);

static void dataf (IDL_output_data *data, const char *fmt, ...)
{
	gchar *buffer;
	va_list args;

	va_start (args, fmt);
	switch (data->mode) {
	case OUTPUT_FILE:
		vfprintf (data->u.o, fmt, args);
		break;

	case OUTPUT_STRING:
		buffer = g_strdup_vprintf (fmt, args);
		g_string_append (data->u.s, buffer);
		g_free (buffer);
		break;

	default:
		break;
	}
	va_end (args);
}

static void idataf (IDL_output_data *data, const char *fmt, ...)
{
	gchar *buffer;
	va_list args;

	va_start (args, fmt);
	doindent ();
	switch (data->mode) {
	case OUTPUT_FILE:
		vfprintf (data->u.o, fmt, args);
		break;

	case OUTPUT_STRING:
		buffer = g_strdup_vprintf (fmt, args);
		g_string_append (data->u.s, buffer);
		g_free (buffer);
		break;

	default:
		break;
	}
	va_end (args);
}

static gboolean IDL_emit_node_pre_func (IDL_tree_func_data *tfd, IDL_output_data *data);
static gboolean IDL_emit_node_post_func (IDL_tree_func_data *tfd, IDL_output_data *data);

typedef struct {
	IDL_tree_func pre_func;
	IDL_tree_func post_func;
	IDL_tree_type type, type2;
	gboolean limit;
	IDL_output_data *data;
	const char *delim;
	gboolean hit;
} IDL_output_delim_data;

static gboolean IDL_output_delim_match (IDL_tree_func_data *tfd, IDL_output_delim_data *delim)
{
	return delim->type == IDLN_ANY ||
		IDL_NODE_TYPE (tfd->tree) == delim->type ||
		IDL_NODE_TYPE (tfd->tree) == delim->type2;
}

static gboolean IDL_output_delim_pre (IDL_tree_func_data *tfd, IDL_output_delim_data *delim)
{
	if (IDL_output_delim_match (tfd, delim)) {
		if (delim->hit)
			dataf (delim->data, "%s", delim->delim);
		else
			delim->hit = TRUE;
		return delim->pre_func
			? (*delim->pre_func) (tfd, delim->data)
			: TRUE;
	} else {
		if (!delim->limit)
			return delim->pre_func
				? (*delim->pre_func) (tfd, delim->data)
				: TRUE;
		else
			return TRUE;
	}
}

static gboolean IDL_output_delim_post (IDL_tree_func_data *tfd, IDL_output_delim_data *delim)
{
	if (delim->limit && !IDL_output_delim_match (tfd, delim))
		return TRUE;

	return delim->post_func
		? (*delim->post_func) (tfd, delim->data)
		: TRUE;
}

static void IDL_output_delim (IDL_tree p, IDL_tree_func_data *current,
			      IDL_output_data *data,
			      IDL_tree_func pre_func, IDL_tree_func post_func,
			      IDL_tree_type type, IDL_tree_type type2,
			      gboolean limit,
			      const char *str)
{
	IDL_output_delim_data delim;

	delim.pre_func = pre_func;
	delim.post_func = post_func;
	delim.type = type;
	delim.type2 = type2;
	delim.limit = limit;
	delim.data = data;
	delim.hit = FALSE;
	delim.delim = str;

	IDL_tree_walk2 (p, current, /*flags*/0,
		       (IDL_tree_func) IDL_output_delim_pre,
		       (IDL_tree_func) IDL_output_delim_post,
		       &delim);
}

typedef struct {
	IDL_output_data *data;
	gboolean hit;
} IDL_property_emit_data;

static void IDL_emit_IDL_property (const char *key, const char *value,
				   IDL_property_emit_data *emit_data)
{
	IDL_output_data *data = emit_data->data;

	if (!emit_data->hit)
		emit_data->hit = TRUE;
	else
		dataf (emit_data->data, DELIM_COMMA);
	if (!data->inline_props) {
		nl ();
		doindent ();
	}
	if (value && *value)
		dataf (emit_data->data, "%s%s(%s)",
		       key, DELIM_SPACE, value);
	else
		dataf (emit_data->data, "%s", key);
}

static gboolean IDL_emit_IDL_properties (IDL_tree p, IDL_output_data *data)
{
	IDL_property_emit_data emit_data;

	if (IDL_NODE_PROPERTIES (p) &&
	    data->flags & IDLF_OUTPUT_PROPERTIES &&
	    g_hash_table_size (IDL_NODE_PROPERTIES (p)) > 0) {
		emit_data.data = data;
		emit_data.hit = FALSE;
		if (!data->inline_props)
			idataf (data, "[" DELIM_SPACE);
		else
			dataf (data, "[");
		indent ();
		g_hash_table_foreach (IDL_NODE_PROPERTIES (p),
				      (GHFunc) IDL_emit_IDL_property,
				      &emit_data);
		unindent ();
		if (!data->inline_props) {
			nl ();
			doindent ();
		}
		dataf (data, "]");
		if (!data->inline_props)
			nl ();
		else
			dataf (data, DELIM_SPACE);
	}

	return TRUE;
}

static gboolean IDL_emit_IDL_sc (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	dataf (data, ";"); nl ();

	return TRUE;
}

static gboolean IDL_emit_IDL_indent (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	doindent ();

	return TRUE;
}

static gboolean IDL_emit_IDL_curly_brace_open (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	dataf (data, "{"); nl (); indent ();

	return TRUE;
}

static gboolean IDL_emit_IDL_curly_brace_close (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	unindent (); idataf (data, "}");

	return TRUE;
}

static gboolean IDL_emit_IDL_curly_brace_close_sc (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_emit_IDL_curly_brace_close (tfd, data);
	IDL_emit_IDL_sc (tfd, data);

	return TRUE;
}

static gboolean IDL_emit_IDL_ident_real (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_tree_func_data *up_path;
	IDL_tree up_real, scope;
	char *s;
	int levels;

	up_path = tfd;
	up_real = tfd->tree;
	while (up_path && up_real) {
		if (IDL_NODE_TYPE (up_path->tree) != IDL_NODE_TYPE (up_real))
			break;
		up_path = up_path->up;
		up_real = IDL_NODE_UP (up_real);
	}

	assert (IDL_NODE_TYPE (tfd->tree) == IDLN_IDENT);

	if (!up_real || data->flags & IDLF_OUTPUT_NO_QUALIFY_IDENTS) {
		/* TODO: If the IDENT is also a keyword, escape it by
		   prepending an underscore. */
		dataf (data, "%s", IDL_IDENT (tfd->tree).str);
	} else {
		if (up_path == NULL) {
		    levels = 0;
		} else {
		    /* Determine minimal required levels of scoping */
		    scope = up_path->tree ? up_path->tree : up_real;
		    levels = IDL_ns_scope_levels_from_here (data->ns, tfd->tree, scope);
		}
		s = IDL_ns_ident_to_qstring (IDL_IDENT_TO_NS (tfd->tree), "::", levels);
		dataf (data, "%s", s);
		g_free (s);
	}

	return TRUE;
}

static gboolean IDL_emit_IDL_ident_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	if (data->idents)
		IDL_emit_IDL_ident_real (tfd, data);

	return TRUE;
}

static gboolean IDL_emit_IDL_ident_force_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_emit_IDL_ident_real (tfd, data);

	return TRUE;
}

static gboolean IDL_emit_IDL_ident (IDL_tree ident, IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_tree_walk2 (ident, tfd, /*flags*/0,
		       (IDL_tree_func) IDL_emit_IDL_ident_real, NULL,
		       data);

	return TRUE;
}

static gboolean IDL_emit_IDL_literal (IDL_tree p, IDL_output_data *data)
{
	switch (IDL_NODE_TYPE (p)) {
	case IDLN_FLOAT:
		dataf (data, "%f", IDL_FLOAT (p).value);
		break;

	case IDLN_INTEGER:
		/* FIXME: sign */
		dataf (data, "%" IDL_LL "d", IDL_INTEGER (p).value);
		break;

	case IDLN_FIXED:
		dataf (data, "%s", IDL_FIXED (p).value);
		break;

	case IDLN_CHAR:
		dataf (data, "'%s'", IDL_CHAR (p).value);
		break;

	case IDLN_WIDE_CHAR:
/*		dataf (data, "'%s'", IDL_WIDE_CHAR (p).value); */
		g_warning ("IDL_emit_IDL_literal: %s is currently unhandled",
			   "Wide character output");
		break;

	case IDLN_BOOLEAN:
		dataf (data, "%s", IDL_BOOLEAN (p).value ? "TRUE" : "FALSE");
		break;

	case IDLN_STRING:
		dataf (data, "\"%s\"", IDL_STRING (p).value);
		break;

	case IDLN_WIDE_STRING:
/*		dataf (data, "\"%s\"", IDL_STRING (p).value); */
		g_warning ("IDL_emit_IDL_literal: %s is currently unhandled",
			   "Wide string output");
		break;

	default:
		g_warning ("Unhandled literal: %s", IDL_NODE_TYPE_NAME (p));
		break;
	}

	return TRUE;
}

static gboolean IDL_emit_IDL_literal_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	if (data->literals)
		IDL_emit_IDL_literal (tfd->tree, data);

	return TRUE;
}

static gboolean IDL_emit_IDL_literal_force_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_emit_IDL_literal (tfd->tree, data);

	return TRUE;
}

static gboolean IDL_emit_IDL_type_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_tree p, q;

	p = tfd->tree;

	switch (IDL_NODE_TYPE (p)) {
	case IDLN_IDENT:
		IDL_emit_IDL_ident (p, tfd, data);
		break;

	case IDLN_TYPE_CHAR:
		dataf (data, "char");
		break;

	case IDLN_TYPE_WIDE_CHAR:
		dataf (data, "wchar");
		break;

	case IDLN_TYPE_BOOLEAN:
		dataf (data, "boolean");
		break;

	case IDLN_TYPE_OCTET:
		dataf (data, "octet");
		break;

	case IDLN_TYPE_ANY:
		dataf (data, "any");
		break;

	case IDLN_TYPE_OBJECT:
		dataf (data, "Object");
		break;

	case IDLN_TYPE_TYPECODE:
		dataf (data, "TypeCode");
		break;

	case IDLN_TYPE_FLOAT:
		switch (IDL_TYPE_FLOAT (p).f_type) {
		case IDL_FLOAT_TYPE_FLOAT: dataf (data, "float"); break;
		case IDL_FLOAT_TYPE_DOUBLE: dataf (data, "double"); break;
		case IDL_FLOAT_TYPE_LONGDOUBLE: dataf (data, "long" DELIM_SPACE "double"); break;
		}
		break;

	case IDLN_TYPE_FIXED:
		dataf (data, "fixed<");
		IDL_emit_IDL_literal (IDL_TYPE_FIXED (p).positive_int_const, data);
		dataf (data, DELIM_COMMA);
		IDL_emit_IDL_literal (IDL_TYPE_FIXED (p).integer_lit, data);
		dataf (data, ">");
		return FALSE;

	case IDLN_TYPE_INTEGER:
		if (!IDL_TYPE_INTEGER (p).f_signed)
			dataf (data, "unsigned" DELIM_SPACE);
		switch (IDL_TYPE_INTEGER (p).f_type) {
		case IDL_INTEGER_TYPE_SHORT: dataf (data, "short"); break;
		case IDL_INTEGER_TYPE_LONG: dataf (data, "long"); break;
		case IDL_INTEGER_TYPE_LONGLONG: dataf (data, "long" DELIM_SPACE "long"); break;
		}
		break;

	case IDLN_TYPE_STRING:
	case IDLN_TYPE_WIDE_STRING:
		if (IDL_NODE_TYPE (p) == IDLN_TYPE_STRING) {
			dataf (data, "string");
			q = IDL_TYPE_STRING (p).positive_int_const;
		} else {
			dataf (data, "wstring");
			q = IDL_TYPE_WIDE_STRING (p).positive_int_const;
		}
		if (q) {
			dataf (data, "<");
			if (IDL_NODE_TYPE (p) == IDLN_TYPE_STRING)
				IDL_emit_IDL_literal (
					IDL_TYPE_STRING (p).positive_int_const, data);
			else
				IDL_emit_IDL_literal (
					IDL_TYPE_WIDE_STRING (p).positive_int_const, data);
			dataf (data, ">");
		}
		return FALSE;

	case IDLN_TYPE_ENUM:
		IDL_emit_IDL_indent (tfd, data);
		data->inline_props = TRUE;
		IDL_emit_IDL_properties (IDL_TYPE_ENUM (tfd->tree).ident, data);
		dataf (data, "enum" DELIM_SPACE);
		IDL_emit_IDL_ident (IDL_TYPE_ENUM (p).ident, tfd, data);
		dataf (data, DELIM_SPACE "{" DELIM_SPACE);
		IDL_output_delim (IDL_TYPE_ENUM (p).enumerator_list, tfd, data,
				  (IDL_tree_func) IDL_emit_IDL_ident_force_pre, NULL,
				  IDLN_IDENT, IDLN_NONE, TRUE, DELIM_COMMA);
		dataf (data, DELIM_SPACE "};"); nl ();
		return FALSE;

	case IDLN_TYPE_ARRAY:
		IDL_emit_IDL_ident (IDL_TYPE_ARRAY (p).ident, tfd, data);
		dataf (data, "[");
		IDL_output_delim (IDL_TYPE_ARRAY (p).size_list, tfd, data,
				  (IDL_tree_func) IDL_emit_IDL_literal_force_pre, NULL,
				  IDLN_INTEGER, IDLN_NONE, TRUE, DELIM_ARRAY);
		dataf (data, "]");
		return FALSE;

	case IDLN_TYPE_SEQUENCE:
		dataf (data, "sequence<");
		save_flag (idents, TRUE);
		IDL_tree_walk2 (IDL_TYPE_SEQUENCE (tfd->tree).simple_type_spec, tfd,/*flags*/0,
			       (IDL_tree_func) IDL_emit_node_pre_func,
			       (IDL_tree_func) IDL_emit_node_post_func,
			       data);
		restore_flag (idents);
		if (IDL_TYPE_SEQUENCE (tfd->tree).positive_int_const) {
			dataf (data, DELIM_COMMA);
			IDL_emit_IDL_literal (
				IDL_TYPE_SEQUENCE (tfd->tree).positive_int_const, data);
		}
		dataf (data, ">");
		return FALSE;

	case IDLN_TYPE_STRUCT:
		if (!data->su_def)
			doindent ();
		save_flag (su_def, TRUE);
		data->inline_props = TRUE;
		IDL_emit_IDL_properties (IDL_TYPE_STRUCT (tfd->tree).ident, data);
		dataf (data, "struct" DELIM_SPACE);
		IDL_emit_IDL_ident (IDL_TYPE_STRUCT (p).ident, tfd, data);
		dataf (data, DELIM_SPACE);
		IDL_emit_IDL_curly_brace_open (tfd, data);
		IDL_tree_walk2 (IDL_TYPE_STRUCT (p).member_list, tfd,/*flags*/0,
			       (IDL_tree_func) IDL_emit_node_pre_func,
			       (IDL_tree_func) IDL_emit_node_post_func,
			       data);
		restore_flag (su_def);
		if (data->su_def)
			IDL_emit_IDL_curly_brace_close (tfd, data);
		else
			IDL_emit_IDL_curly_brace_close_sc (tfd, data);
		return FALSE;

	case IDLN_TYPE_UNION:
		if (!data->su_def)
			doindent ();
		save_flag (su_def, TRUE);
		data->inline_props = TRUE;
		IDL_emit_IDL_properties (IDL_TYPE_UNION (tfd->tree).ident, data);
		dataf (data, "union" DELIM_SPACE);
		IDL_emit_IDL_ident (IDL_TYPE_UNION (p).ident, tfd, data);
		dataf (data, DELIM_SPACE);
		dataf (data, "switch" DELIM_SPACE "(");
		save_flag (idents, TRUE);
		IDL_tree_walk2 (IDL_TYPE_UNION (p).switch_type_spec, tfd,/*flags*/0,
			       (IDL_tree_func) IDL_emit_node_pre_func,
			       (IDL_tree_func) IDL_emit_node_post_func,
			       data);
		restore_flag (idents);
		dataf (data, ")" DELIM_SPACE "{"); nl ();
		IDL_tree_walk2 (IDL_TYPE_UNION (p).switch_body, tfd,/*flags*/0,
			       (IDL_tree_func) IDL_emit_node_pre_func,
			       (IDL_tree_func) IDL_emit_node_post_func,
			       data);
		restore_flag (su_def);
		if (data->su_def)
			idataf (data, "}");
		else {
			idataf (data, "};");
			nl ();
		}
		return FALSE;

	default:
		break;
	}

	return TRUE;
}

static gboolean IDL_emit_IDL_module_all (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	if (tfd->step == 0) {
		idataf (data, "module" DELIM_SPACE);
		IDL_emit_IDL_ident (IDL_MODULE (tfd->tree).ident, tfd, data);
		dataf (data, DELIM_SPACE);
		IDL_emit_IDL_curly_brace_open (tfd, data);
		save_flag (idents, FALSE);
	} else {
		restore_flag (idents);
		IDL_emit_IDL_curly_brace_close_sc (tfd, data);
	}

	return TRUE;
}

static gboolean IDL_emit_IDL_interface_all (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	if (tfd->step == 0) {
		data->inline_props = FALSE;
		IDL_emit_IDL_properties (IDL_INTERFACE (tfd->tree).ident, data);
		idataf (data, "interface" DELIM_SPACE);
		IDL_emit_IDL_ident (IDL_INTERFACE (tfd->tree).ident, tfd, data);
		dataf (data, DELIM_SPACE);
		if (IDL_INTERFACE (tfd->tree).inheritance_spec) {
			dataf (data, ":" DELIM_SPACE);
			IDL_output_delim (IDL_INTERFACE (tfd->tree).inheritance_spec, tfd, data,
					  (IDL_tree_func) IDL_emit_IDL_ident_force_pre, NULL,
					  IDLN_IDENT, IDLN_NONE, TRUE, DELIM_COMMA);
			dataf (data, DELIM_SPACE);
		}
		IDL_emit_IDL_curly_brace_open (tfd, data);
		save_flag (idents, FALSE);
	} else {
		restore_flag (idents);
		IDL_emit_IDL_curly_brace_close_sc (tfd, data);
	}

	return TRUE;
}

static gboolean IDL_emit_IDL_forward_dcl_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	idataf (data, "interface" DELIM_SPACE);
	IDL_emit_IDL_ident (IDL_FORWARD_DCL (tfd->tree).ident, tfd, data);

	return TRUE;
}

static gboolean IDL_emit_IDL_attr_dcl_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_emit_IDL_indent (tfd, data);
	data->inline_props = TRUE;
	IDL_emit_IDL_properties (IDL_LIST (IDL_ATTR_DCL (tfd->tree).simple_declarations).data,
				 data);
	if (IDL_ATTR_DCL (tfd->tree).f_readonly) dataf (data, "readonly" DELIM_SPACE);
	dataf (data, "attribute" DELIM_SPACE);
	save_flag (idents, TRUE);
	IDL_tree_walk2 (IDL_ATTR_DCL (tfd->tree).param_type_spec, tfd,/*flags*/0,
		       (IDL_tree_func) IDL_emit_node_pre_func,
		       (IDL_tree_func) IDL_emit_node_post_func,
		       data);
	restore_flag (idents);
	dataf (data, DELIM_SPACE);
	IDL_output_delim (IDL_ATTR_DCL (tfd->tree).simple_declarations, tfd, data,
			  (IDL_tree_func) IDL_emit_IDL_ident_force_pre, NULL,
			  IDLN_IDENT, IDLN_NONE, TRUE, DELIM_COMMA);
	IDL_emit_IDL_sc (tfd, data);

	return FALSE;
}

static gboolean IDL_emit_IDL_op_dcl_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_emit_IDL_indent (tfd, data);
	data->inline_props = TRUE;
	IDL_emit_IDL_properties (IDL_OP_DCL (tfd->tree).ident, data);
	if (IDL_OP_DCL (tfd->tree).f_oneway) dataf (data, "oneway" DELIM_SPACE);
	if (IDL_OP_DCL (tfd->tree).op_type_spec) {
		save_flag (idents, TRUE);
		IDL_tree_walk2 (IDL_OP_DCL (tfd->tree).op_type_spec, tfd,/*flags*/0,
			       (IDL_tree_func) IDL_emit_node_pre_func,
			       (IDL_tree_func) IDL_emit_node_post_func,
			       data);
		restore_flag (idents);
	} else
		dataf (data, "void");
	dataf (data, DELIM_SPACE "%s" DELIM_SPACE "(",
	       IDL_IDENT (IDL_OP_DCL (tfd->tree).ident).str);
	if (IDL_OP_DCL (tfd->tree).parameter_dcls)
		IDL_output_delim (IDL_OP_DCL (tfd->tree).parameter_dcls, tfd, data,
				  (IDL_tree_func) IDL_emit_node_pre_func,
				  (IDL_tree_func) IDL_emit_node_post_func,
				  IDLN_PARAM_DCL, IDLN_NONE, FALSE, DELIM_COMMA);
	if (IDL_OP_DCL (tfd->tree).f_varargs)
		dataf (data, DELIM_COMMA "...");
	dataf (data, ")");
	if (IDL_OP_DCL (tfd->tree).raises_expr) {
		nl (); indent ();
		idataf (data, DELIM_SPACE "raises" DELIM_SPACE "(");
		IDL_output_delim (IDL_OP_DCL (tfd->tree).raises_expr, tfd, data,
				  (IDL_tree_func) IDL_emit_IDL_ident_force_pre, NULL,
				  IDLN_IDENT, IDLN_NONE, TRUE, DELIM_COMMA);
		dataf (data, ")");
		unindent ();
	}
	if (IDL_OP_DCL (tfd->tree).context_expr) {
		nl (); indent ();
		idataf (data, DELIM_SPACE "context" DELIM_SPACE "(");
		IDL_output_delim (IDL_OP_DCL (tfd->tree).context_expr, tfd, data,
				  (IDL_tree_func) IDL_emit_IDL_literal_force_pre, NULL,
				  IDLN_STRING, IDLN_NONE, TRUE, DELIM_COMMA);
		dataf (data, ")");
		unindent ();
	}
	IDL_emit_IDL_sc (tfd, data);

	return FALSE;
}

static gboolean IDL_emit_IDL_param_dcl_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	data->inline_props = TRUE;
	IDL_emit_IDL_properties (IDL_PARAM_DCL (tfd->tree).simple_declarator, data);
	switch (IDL_PARAM_DCL (tfd->tree).attr) {
	case IDL_PARAM_IN: dataf (data, "in" DELIM_SPACE); break;
	case IDL_PARAM_OUT: dataf (data, "out" DELIM_SPACE); break;
	case IDL_PARAM_INOUT: dataf (data, "inout" DELIM_SPACE); break;
	}
	save_flag (idents, TRUE);
	IDL_tree_walk2 (IDL_PARAM_DCL (tfd->tree).param_type_spec, tfd,/*flags*/0,
		       (IDL_tree_func) IDL_emit_node_pre_func,
		       (IDL_tree_func) IDL_emit_node_post_func,
		       data);
	restore_flag (idents);
	dataf (data, DELIM_SPACE);
	IDL_emit_IDL_ident (IDL_PARAM_DCL (tfd->tree).simple_declarator, tfd, data);

	return FALSE;
}

static gboolean IDL_emit_IDL_type_dcl_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_tree_func_data down_tfd;
	IDL_tree q;

	IDL_emit_IDL_indent (tfd, data);
	data->inline_props = TRUE;
	IDL_emit_IDL_properties (IDL_LIST (IDL_TYPE_DCL (tfd->tree).dcls).data, data);
	dataf (data, "typedef" DELIM_SPACE);
	save_flag (idents, TRUE);
	save_flag (su_def, TRUE);
	IDL_tree_walk2 (IDL_TYPE_DCL (tfd->tree).type_spec, tfd,/*flags*/0,
		       (IDL_tree_func) IDL_emit_node_pre_func,
		       (IDL_tree_func) IDL_emit_node_post_func,
		       data);
	dataf (data, DELIM_SPACE);
	down_tfd = *tfd;
	down_tfd.up = tfd;
	for (q = IDL_TYPE_DCL (tfd->tree).dcls; q; q = IDL_LIST (q).next) {
		down_tfd.tree = q;
		IDL_tree_walk2 (IDL_LIST (q).data, &down_tfd,/*flags*/0,
			       (IDL_tree_func) IDL_emit_node_pre_func,
			       (IDL_tree_func) IDL_emit_node_post_func,
			       data);
		if (IDL_LIST (q).next)
			dataf (data, DELIM_COMMA);
	}
	restore_flag (idents);
	restore_flag (su_def);
	IDL_emit_IDL_sc (tfd, data);

	return FALSE;
}

static gboolean IDL_emit_IDL_const_dcl_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	idataf (data, "const" DELIM_SPACE);
	save_flag (idents, TRUE);
	IDL_tree_walk2 (IDL_CONST_DCL (tfd->tree).const_type, tfd,/*flags*/0,
		       (IDL_tree_func) IDL_emit_node_pre_func,
		       (IDL_tree_func) IDL_emit_node_post_func,
		       data);
	restore_flag (idents);
	dataf (data, DELIM_SPACE);
	IDL_emit_IDL_ident (IDL_CONST_DCL (tfd->tree).ident, tfd, data);
	dataf (data, DELIM_SPACE "=" DELIM_SPACE);
	save_flag (literals, TRUE);
	IDL_tree_walk2 (IDL_CONST_DCL (tfd->tree).const_exp, tfd,/*flags*/0,
		       (IDL_tree_func) IDL_emit_node_pre_func,
		       (IDL_tree_func) IDL_emit_node_post_func,
		       data);
	restore_flag (literals);
	IDL_emit_IDL_sc (tfd, data);

	return FALSE;
}

static gboolean IDL_emit_IDL_except_dcl_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	idataf (data, "exception" DELIM_SPACE);
	IDL_emit_IDL_ident (IDL_EXCEPT_DCL (tfd->tree).ident, tfd, data);
	dataf (data, DELIM_SPACE);
	IDL_emit_IDL_curly_brace_open (tfd, data);

	return TRUE;
}

static gboolean IDL_emit_IDL_native_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_emit_IDL_indent (tfd, data);
	data->inline_props = TRUE;
	IDL_emit_IDL_properties (IDL_NATIVE (tfd->tree).ident, data);
	dataf (data, "native" DELIM_SPACE);
	IDL_emit_IDL_ident (IDL_NATIVE (tfd->tree).ident, tfd, data);
	if (IDL_NATIVE (tfd->tree).user_type)
		dataf (data, DELIM_SPACE "(%s)", IDL_NATIVE (tfd->tree).user_type);
	IDL_emit_IDL_sc (tfd, data);

	return TRUE;
}

static gboolean IDL_emit_IDL_case_stmt_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_tree_func_data down_tfd;
	IDL_tree q;

	save_flag (idents, TRUE);
	save_flag (literals, TRUE);
	down_tfd = *tfd;
	down_tfd.up = tfd;
	for (q = IDL_CASE_STMT (tfd->tree).labels; q; q = IDL_LIST (q).next) {
		if (IDL_LIST (q).data) {
			down_tfd.tree = q;
			idataf (data, "case" DELIM_SPACE);
			IDL_tree_walk2 (IDL_LIST (q).data, &down_tfd,/*flags*/0,
				       (IDL_tree_func) IDL_emit_node_pre_func,
				       (IDL_tree_func) IDL_emit_node_post_func,
				       data);
			dataf (data, ":");
		} else
			idataf (data, "default:");
		nl ();
	}
	restore_flag (literals);
	restore_flag (idents);
	indent ();

	return FALSE;
}

static gboolean IDL_emit_IDL_case_stmt_post (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_tree_walk2 (IDL_CASE_STMT (tfd->tree).element_spec, tfd, /*flags*/0,
		       (IDL_tree_func) IDL_emit_node_pre_func,
		       (IDL_tree_func) IDL_emit_node_post_func,
		       data);
	unindent ();

	return FALSE;
}

static gboolean IDL_emit_IDL_member_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	IDL_emit_IDL_indent (tfd, data);
	save_flag (idents, TRUE);
	IDL_tree_walk2 (IDL_MEMBER (tfd->tree).type_spec, tfd, /*flags*/0,
		       (IDL_tree_func) IDL_emit_node_pre_func,
		       (IDL_tree_func) IDL_emit_node_post_func,
		       data);
	restore_flag (idents);

	return FALSE;
}

static gboolean IDL_emit_IDL_member_post (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	dataf (data, DELIM_SPACE);
	IDL_output_delim (IDL_MEMBER (tfd->tree).dcls, tfd, data,
			  (IDL_tree_func) IDL_emit_IDL_type_pre, NULL,
			  IDLN_IDENT, IDLN_TYPE_ARRAY, FALSE, DELIM_COMMA);
	IDL_emit_IDL_sc (tfd, data);

	return TRUE;
}

static gboolean IDL_emit_IDL_codefrag_pre (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	if (data->flags & IDLF_OUTPUT_CODEFRAGS) {
		GSList *slist;

		dataf (data, "%%{ %s", IDL_CODEFRAG (tfd->tree).desc); nl ();
		for (slist = IDL_CODEFRAG (tfd->tree).lines; slist; slist = slist->next) {
			dataf (data, "%s", (char *) slist->data); nl ();
		}
		dataf (data, "%%}"); nl ();
	}

	return TRUE;
}

struct IDL_emit_node {
	IDL_tree_func pre;
	IDL_tree_func post;
};

static struct IDL_emit_node * IDL_get_IDL_emission_table (void)
{
	static gboolean initialized = FALSE;
	static struct IDL_emit_node table[IDLN_LAST];
	struct IDL_emit_node *s;

	if (!initialized) {

		s = &table[IDLN_MODULE];
		s->pre = s->post = (IDL_tree_func) IDL_emit_IDL_module_all;

		s = &table[IDLN_INTERFACE];
		s->pre = s->post = (IDL_tree_func) IDL_emit_IDL_interface_all;

		s = &table[IDLN_FORWARD_DCL];
		s->pre = (IDL_tree_func) IDL_emit_IDL_forward_dcl_pre;
		s->post = (IDL_tree_func) IDL_emit_IDL_sc;

		s = &table[IDLN_ATTR_DCL];
		s->pre = (IDL_tree_func) IDL_emit_IDL_attr_dcl_pre;

		s = &table[IDLN_OP_DCL];
		s->pre = (IDL_tree_func) IDL_emit_IDL_op_dcl_pre;

		s = &table[IDLN_PARAM_DCL];
		s->pre = (IDL_tree_func) IDL_emit_IDL_param_dcl_pre;

		s = &table[IDLN_TYPE_DCL];
		s->pre = (IDL_tree_func) IDL_emit_IDL_type_dcl_pre;

		s = &table[IDLN_CONST_DCL];
		s->pre = (IDL_tree_func) IDL_emit_IDL_const_dcl_pre;

		s = &table[IDLN_EXCEPT_DCL];
		s->pre = (IDL_tree_func) IDL_emit_IDL_except_dcl_pre;
		s->post = (IDL_tree_func) IDL_emit_IDL_curly_brace_close_sc;

		s = &table[IDLN_MEMBER];
		s->pre = (IDL_tree_func) IDL_emit_IDL_member_pre;
		s->post = (IDL_tree_func) IDL_emit_IDL_member_post;

		s = &table[IDLN_NATIVE];
		s->pre = (IDL_tree_func) IDL_emit_IDL_native_pre;

		s = &table[IDLN_CASE_STMT];
		s->pre = (IDL_tree_func) IDL_emit_IDL_case_stmt_pre;
		s->post = (IDL_tree_func) IDL_emit_IDL_case_stmt_post;

		s = &table[IDLN_IDENT];
		s->pre = (IDL_tree_func) IDL_emit_IDL_ident_pre;

		s = &table[IDLN_CODEFRAG];
		s->pre = (IDL_tree_func) IDL_emit_IDL_codefrag_pre;

		table[IDLN_TYPE_FLOAT].pre =
			table[IDLN_TYPE_CHAR].pre =
			table[IDLN_TYPE_WIDE_CHAR].pre =
			table[IDLN_TYPE_BOOLEAN].pre =
			table[IDLN_TYPE_OCTET].pre =
			table[IDLN_TYPE_ANY].pre =
			table[IDLN_TYPE_OBJECT].pre =
			table[IDLN_TYPE_TYPECODE].pre =
			table[IDLN_TYPE_FIXED].pre =
			table[IDLN_TYPE_INTEGER].pre =
			table[IDLN_TYPE_STRING].pre =
			table[IDLN_TYPE_WIDE_STRING].pre =
			table[IDLN_TYPE_ENUM].pre =
			table[IDLN_TYPE_ARRAY].pre =
			table[IDLN_TYPE_SEQUENCE].pre =
			table[IDLN_TYPE_STRUCT].pre =
			table[IDLN_TYPE_UNION].pre = (IDL_tree_func) IDL_emit_IDL_type_pre;

		table[IDLN_FLOAT].pre =
			table[IDLN_INTEGER].pre =
			table[IDLN_FIXED].pre =
			table[IDLN_CHAR].pre =
			table[IDLN_WIDE_CHAR].pre =
			table[IDLN_BOOLEAN].pre =
			table[IDLN_STRING].pre =
			table[IDLN_WIDE_STRING].pre = (IDL_tree_func) IDL_emit_IDL_literal_pre;

		initialized = TRUE;
	}

	return table;
}

static gboolean IDL_emit_node_pre_func (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	const struct IDL_emit_node * const s =
		&IDL_get_IDL_emission_table () [IDL_NODE_TYPE (tfd->tree)];

	if (s->pre)
		return (*s->pre) (tfd, data);

	return TRUE;
}

static gboolean IDL_emit_node_post_func (IDL_tree_func_data *tfd, IDL_output_data *data)
{
	const struct IDL_emit_node * const s =
		&IDL_get_IDL_emission_table () [IDL_NODE_TYPE (tfd->tree)];

	if (s->post)
		return (*s->post) (tfd, data);

	return TRUE;
}

void IDL_tree_to_IDL (IDL_tree p, IDL_ns ns, FILE *output, unsigned long output_flags)
{
	IDL_output_data data;

	g_return_if_fail (output != NULL);

	data.ns = ns;
	data.mode = OUTPUT_FILE;
	data.u.o = output;
	data.flags = output_flags;
	data.ilev = 0;
	data.idents = TRUE;
	data.literals = TRUE;
	data.inline_props = TRUE;
	data.su_def = FALSE;

	if (ns == NULL)
		data.flags |= IDLF_OUTPUT_NO_QUALIFY_IDENTS;

	IDL_tree_walk2 (p, NULL, /*flags*/0,
		       (IDL_tree_func) IDL_emit_node_pre_func,
		       (IDL_tree_func) IDL_emit_node_post_func,
		       &data);
}

GString *IDL_tree_to_IDL_string (IDL_tree p, IDL_ns ns, unsigned long output_flags)
{
	IDL_output_data data;

	data.ns = ns;
	data.mode = OUTPUT_STRING;
	data.u.s = g_string_new (NULL);
	data.flags = output_flags;
	data.ilev = 0;
	data.idents = TRUE;
	data.literals = TRUE;
	data.inline_props = TRUE;
	data.su_def = FALSE;

	if (ns == NULL)
		data.flags |= IDLF_OUTPUT_NO_QUALIFY_IDENTS;

	IDL_tree_walk2 (p, NULL, /*flags*/0,
		       (IDL_tree_func) IDL_emit_node_pre_func,
		       (IDL_tree_func) IDL_emit_node_post_func,
		       &data);

	return data.u.s;
}

typedef struct {
    IDL_tree	searchNode;
    gboolean	found;
} ContainsNodeWalkerInfo;

static gboolean 
contains_node_walker (IDL_tree_func_data *tfd, gpointer data) {
	ContainsNodeWalkerInfo	*info = data;
	/* skip root! */
	if (tfd->up != NULL && tfd->tree == info->searchNode) {
		info->found = TRUE;
		return FALSE;
	}
 	return TRUE;	/* continue walking */
}

gboolean IDL_tree_contains_node(IDL_tree tree, IDL_tree node) {
	ContainsNodeWalkerInfo	info;
	info.searchNode = node;
	info.found = 0;
	IDL_tree_walk2 (tree, /*curwalk*/NULL, /*flags*/0,
	  /*pre*/contains_node_walker, /*post*/NULL, &info);
	return info.found;
}

struct IDL_recursive_walker_info {
	GSList   *ident_list;
	gboolean  recursive;
	};

/*
 * IDL_tree_is_recursive_walker_pre:
 * @walk_frame: a #IDL_tree_func_data structure
 * @user_data: a #IDL_recursive_walker_info structure.
 *
 * If the current node in the parse tree is a structure or union
 * add its IDLN_IDENT to the ident list contained in @user_data;
 *
 * If the current node in the parse tree is a sequence, check to
 * see if the sequence element type is on the list of IDLN_IDENTs.
 *
 * Return value: %FALSE if recursion is detected - this stops the
 *               walk to the parse tree -, %TRUE otherwise.
 */
static gboolean
IDL_tree_is_recursive_walker_pre (IDL_tree_func_data *walk_frame,
				  gpointer            user_data)
{
	struct IDL_recursive_walker_info *info = user_data;
	IDL_tree                          node = walk_frame->tree;

	switch (IDL_NODE_TYPE (node)) {
	case IDLN_TYPE_STRUCT:
	case IDLN_TYPE_UNION:
		info->ident_list = g_slist_prepend (info->ident_list,
						    IDL_TYPE_STRUCT (node).ident);
		break;
	case IDLN_TYPE_SEQUENCE: {
		IDL_tree  seq_type = IDL_TYPE_SEQUENCE (node).simple_type_spec;
		GSList   *l = info->ident_list;

		if (IDL_NODE_TYPE (seq_type) != IDLN_IDENT)
			break;

		g_assert (IDL_IDENT (seq_type).repo_id);

		for (; l; l = l->next) {
			IDL_tree n = (IDL_tree)l->data;

			g_assert (IDL_IDENT (n).repo_id);

			if (!strcmp (IDL_IDENT (n).repo_id,
				     IDL_IDENT (seq_type).repo_id)) {

				info->recursive = TRUE;
				return FALSE;
			}
		}
		}
		break;
	default:
		break;
	}

	return TRUE;
}

/*
 * IDL_tree_is_recursive_walker_post:
 * @walk_frame: a #IDL_tree_func_data structure
 * @user_data: a #IDL_recursive_walker_info structure.
 *
 * If the current node in the parse tree is a structure or
 * union, remove the IDLN_IDENT from the ident list.
 *
 * Return value: %TRUE.
 */
static gboolean
IDL_tree_is_recursive_walker_post (IDL_tree_func_data *walk_frame,
				   gpointer            user_data)
{
	struct IDL_recursive_walker_info *info = user_data;
	IDL_tree                          node = walk_frame->tree;

	switch (IDL_NODE_TYPE (node)) {
	case IDLN_TYPE_STRUCT:
	case IDLN_TYPE_UNION: {
		GSList *link = info->ident_list;

		g_assert (((IDL_tree)link->data) == IDL_TYPE_STRUCT (node).ident);

		info->ident_list = g_slist_remove_link (info->ident_list,
						        link);
		g_slist_free_1 (link);
		}
		break;
	default:
		break;
	}

	return TRUE;
}

/*
 * IDL_tree_is_recursive:
 * @tree: a #IDL_tree.
 * @hasRecur: dummy argument, not used, deprecated, compatibility cruft.
 *
 * Checks whether or not @tree contains recursive types.
 *
 * Recursive types may only be constructed using structs/unions which
 * contain sequences that hold the parent struct/union type.
 *
 * Return value: %TRUE if @tree contains recursive types,
 *               %FALSE otherwise.
 */
gboolean
IDL_tree_is_recursive (IDL_tree tree, gpointer dummy)
{
	struct IDL_recursive_walker_info info;

	info.ident_list = NULL;
	info.recursive  = FALSE;

	IDL_tree_walk2 (tree, NULL, IDL_WalkF_TypespecOnly, 
			IDL_tree_is_recursive_walker_pre, 
			IDL_tree_is_recursive_walker_post, 
			&info);

	g_assert (!info.ident_list);

	return info.recursive;
}

/*
 * Local variables:
 * mode: C
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 */
