Blob Blame History Raw
From 0b31424ac714ccff963defe8401f20357fc6bcce Mon Sep 17 00:00:00 2001
From: James Carter <jwcart2@gmail.com>
Date: Wed, 21 Apr 2021 13:21:10 -0400
Subject: [PATCH] libsepol/cil: Create functions to write the CIL AST

The function cil_print_tree() has existed in cil_tree.c since the
beginning of the development of CIL and secilc. Unfortunately, it
used cil_log() at log level CIL_INFO to print out the AST and
has suffered greatly from bit rot.

Move the functions to write the CIL AST to cil_write_ast.c, update
the functions, and write the AST to the FILE pointer passed in as
an argument.

The function cil_write_ast() supports writing the CIL AST at three
different phases of the compiling a CIL policy. After parsing has
been done and the parse tree has been created, after the CIL AST
has been built, and after the CIL AST has been resolved.

Signed-off-by: James Carter <jwcart2@gmail.com>
---
 libsepol/cil/src/cil_tree.c      | 1471 ----------------------------
 libsepol/cil/src/cil_tree.h      |    2 -
 libsepol/cil/src/cil_write_ast.c | 1573 ++++++++++++++++++++++++++++++
 libsepol/cil/src/cil_write_ast.h |   46 +
 4 files changed, 1619 insertions(+), 1473 deletions(-)
 create mode 100644 libsepol/cil/src/cil_write_ast.c
 create mode 100644 libsepol/cil/src/cil_write_ast.h

diff --git a/libsepol/cil/src/cil_tree.c b/libsepol/cil/src/cil_tree.c
index 3da972e9cf4e..067268eb897c 100644
--- a/libsepol/cil/src/cil_tree.c
+++ b/libsepol/cil/src/cil_tree.c
@@ -41,15 +41,6 @@
 #include "cil_parser.h"
 #include "cil_strpool.h"
 
-void cil_tree_print_perms_list(struct cil_tree_node *current_perm);
-void cil_tree_print_classperms(struct cil_classperms *cp);
-void cil_tree_print_level(struct cil_level *level);
-void cil_tree_print_levelrange(struct cil_levelrange *lvlrange);
-void cil_tree_print_context(struct cil_context *context);
-void cil_tree_print_expr_tree(struct cil_tree_node *expr_root);
-void cil_tree_print_constrain(struct cil_constrain *cons);
-void cil_tree_print_node(struct cil_tree_node *node);
-
 __attribute__((noreturn)) __attribute__((format (printf, 1, 2))) void cil_tree_error(const char* msg, ...)
 {
 	va_list ap;
@@ -328,1465 +319,3 @@ int cil_tree_walk(struct cil_tree_node *node,
 
 	return SEPOL_OK;
 }
-
-
-/* Copied from cil_policy.c, but changed to prefix -- Need to refactor */
-static int cil_expr_to_string(struct cil_list *expr, char **out)
-{
-	int rc = SEPOL_ERR;
-	struct cil_list_item *curr;
-	char *stack[COND_EXPR_MAXDEPTH] = {};
-	int pos = 0;
-
-	cil_list_for_each(curr, expr) {
-		if (pos >= COND_EXPR_MAXDEPTH) {
-			rc = SEPOL_ERR;
-			goto exit;
-		}
-		switch (curr->flavor) {
-		case CIL_LIST:
-			rc = cil_expr_to_string(curr->data, &stack[pos]);
-			if (rc != SEPOL_OK) {
-				goto exit;
-			}
-			pos++;
-			break;
-		case CIL_STRING:
-			stack[pos] = curr->data;
-			pos++;
-			break;
-		case CIL_DATUM:
-			stack[pos] = ((struct cil_symtab_datum *)curr->data)->name;
-			pos++;
-			break;
-		case CIL_OP: {
-			int len;
-			char *expr_str;
-			enum cil_flavor op_flavor = *((enum cil_flavor *)curr->data);
-			char *op_str = NULL;
-
-			if (pos == 0) {
-				rc = SEPOL_ERR;
-				goto exit;
-			}
-			switch (op_flavor) {
-			case CIL_AND:
-				op_str = CIL_KEY_AND;
-				break;
-			case CIL_OR:
-				op_str = CIL_KEY_OR;
-				break;
-			case CIL_NOT:
-				op_str = CIL_KEY_NOT;
-				break;
-			case CIL_ALL:
-				op_str = CIL_KEY_ALL;
-				break;
-			case CIL_EQ:
-				op_str = CIL_KEY_EQ;
-				break;
-			case CIL_NEQ:
-				op_str = CIL_KEY_NEQ;
-				break;
-			case CIL_XOR:
-				op_str = CIL_KEY_XOR;
-				break;
-			case CIL_RANGE:
-				op_str = CIL_KEY_RANGE;
-				break;
-			case CIL_CONS_DOM:
-				op_str = CIL_KEY_CONS_DOM;
-				break;
-			case CIL_CONS_DOMBY:
-				op_str = CIL_KEY_CONS_DOMBY;
-				break;
-			case CIL_CONS_INCOMP:
-				op_str = CIL_KEY_CONS_INCOMP;
-				break;
-			default:
-				cil_log(CIL_ERR, "Unknown operator in expression\n");
-				goto exit;
-				break;
-			}
-			if (op_flavor == CIL_NOT) {
-				len = strlen(stack[pos-1]) + strlen(op_str) + 4;
-				expr_str = cil_malloc(len);
-				snprintf(expr_str, len, "(%s %s)", op_str, stack[pos-1]);
-				free(stack[pos-1]);
-				stack[pos-1] = NULL;
-				pos--;
-			} else {
-				if (pos < 2) {
-					rc = SEPOL_ERR;
-					goto exit;
-				}
-				len = strlen(stack[pos-1]) + strlen(stack[pos-2]) + strlen(op_str) + 5;
-				expr_str = cil_malloc(len);
-				snprintf(expr_str, len, "(%s %s %s)", op_str, stack[pos-1], stack[pos-2]);
-				free(stack[pos-2]);
-				free(stack[pos-1]);
-				stack[pos-2] = NULL;
-				stack[pos-1] = NULL;
-				pos -= 2;
-			}
-			stack[pos] = expr_str;
-			pos++;
-			break;
-		}
-		case CIL_CONS_OPERAND: {
-			enum cil_flavor operand_flavor = *((enum cil_flavor *)curr->data);
-			char *operand_str = NULL;
-			switch (operand_flavor) {
-			case CIL_CONS_U1:
-				operand_str = CIL_KEY_CONS_U1;
-				break;
-			case CIL_CONS_U2:
-				operand_str = CIL_KEY_CONS_U2;
-				break;
-			case CIL_CONS_U3:
-				operand_str = CIL_KEY_CONS_U3;
-				break;
-			case CIL_CONS_T1:
-				operand_str = CIL_KEY_CONS_T1;
-				break;
-			case CIL_CONS_T2:
-				operand_str = CIL_KEY_CONS_T2;
-				break;
-			case CIL_CONS_T3:
-				operand_str = CIL_KEY_CONS_T3;
-				break;
-			case CIL_CONS_R1:
-				operand_str = CIL_KEY_CONS_R1;
-				break;
-			case CIL_CONS_R2:
-				operand_str = CIL_KEY_CONS_R2;
-				break;
-			case CIL_CONS_R3:
-				operand_str = CIL_KEY_CONS_R3;
-				break;
-			case CIL_CONS_L1:
-				operand_str = CIL_KEY_CONS_L1;
-				break;
-			case CIL_CONS_L2:
-				operand_str = CIL_KEY_CONS_L2;
-				break;
-			case CIL_CONS_H1:
-				operand_str = CIL_KEY_CONS_H1;
-				break;
-			case CIL_CONS_H2:
-				operand_str = CIL_KEY_CONS_H2;
-				break;
-			default:
-				cil_log(CIL_ERR, "Unknown operand in expression\n");
-				goto exit;
-				break;
-			}
-			stack[pos] = operand_str;
-			pos++;
-			break;
-		}
-		default:
-			cil_log(CIL_ERR, "Unknown flavor in expression\n");
-			goto exit;
-			break;
-		}
-	}
-
-	*out = stack[0];
-
-	return SEPOL_OK;
-
-exit:
-	return rc;
-}
-
-void cil_tree_print_expr(struct cil_list *datum_expr, struct cil_list *str_expr)
-{
-	char *expr_str;
-	int rc;
-
-	cil_log(CIL_INFO, "(");
-
-	if (datum_expr != NULL) {
-		rc = cil_expr_to_string(datum_expr, &expr_str);
-	} else {
-		rc = cil_expr_to_string(str_expr, &expr_str);
-	}
-	if (rc != SEPOL_OK) {
-		cil_log(CIL_INFO, "ERROR)");
-		return;
-	}
-	cil_log(CIL_INFO, "%s)", expr_str);
-	free(expr_str);
-}
-
-void cil_tree_print_perms_list(struct cil_tree_node *current_perm)
-{
-	while (current_perm != NULL) {
-		if (current_perm->flavor == CIL_PERM) {
-			cil_log(CIL_INFO, " %s", ((struct cil_perm *)current_perm->data)->datum.name);
-		} else if (current_perm->flavor == CIL_MAP_PERM) {
-			cil_log(CIL_INFO, " %s", ((struct cil_perm*)current_perm->data)->datum.name);
-		} else {
-			cil_log(CIL_INFO, "\n\n perms list contained unexpected data type: %d\n", current_perm->flavor);
-			break;
-		}
-		current_perm = current_perm->next;	
-	}
-}
-
-void cil_tree_print_cats(struct cil_cats *cats)
-{
-	cil_tree_print_expr(cats->datum_expr, cats->str_expr);
-}
-
-void cil_tree_print_perm_strs(struct cil_list *perm_strs)
-{
-	struct cil_list_item *curr;
-
-	if (perm_strs == NULL) {
-		return;
-	}
-
-	cil_log(CIL_INFO, " (");
-
-	cil_list_for_each(curr, perm_strs) {
-		cil_log(CIL_INFO, " %s", (char*)curr->data);
-	}
-
-	cil_log(CIL_INFO, " )");
-}
-
-
-void cil_tree_print_classperms(struct cil_classperms *cp)
-{
-	if (cp == NULL) {
-		return;
-	}
-
-	cil_log(CIL_INFO, " class: %s", cp->class_str);
-	cil_log(CIL_INFO, ", perm_strs:");
-	cil_tree_print_perm_strs(cp->perm_strs);
-}
-
-void cil_tree_print_classperms_set(struct cil_classperms_set *cp_set)
-{
-	if (cp_set == NULL) {
-		return;
-	}
-
-	cil_log(CIL_INFO, " %s", cp_set->set_str);
-}
-
-void cil_tree_print_classperms_list(struct cil_list *cp_list)
-{
-	struct cil_list_item *i;
-
-	if (cp_list == NULL) {
-		return;
-	}
-
-	cil_list_for_each(i, cp_list) {
-		if (i->flavor == CIL_CLASSPERMS) {
-			cil_tree_print_classperms(i->data);
-		} else {
-			cil_tree_print_classperms_set(i->data);
-		}
-	}
-}
-
-void cil_tree_print_level(struct cil_level *level)
-{
-	if (level->sens != NULL) {
-		cil_log(CIL_INFO, " %s", level->sens->datum.name);
-	} else if (level->sens_str != NULL) {
-		cil_log(CIL_INFO, " %s", level->sens_str);
-	}
-
-	cil_tree_print_cats(level->cats);
-
-	return;
-}
-
-void cil_tree_print_levelrange(struct cil_levelrange *lvlrange)
-{
-	cil_log(CIL_INFO, " (");
-	if (lvlrange->low != NULL) {
-		cil_log(CIL_INFO, " (");
-		cil_tree_print_level(lvlrange->low);
-		cil_log(CIL_INFO, " )");
-	} else if (lvlrange->low_str != NULL) {
-		cil_log(CIL_INFO, " %s", lvlrange->low_str);
-	}
-
-	if (lvlrange->high != NULL) {
-		cil_log(CIL_INFO, " (");
-		cil_tree_print_level(lvlrange->high);
-		cil_log(CIL_INFO, " )");
-	} else if (lvlrange->high_str != NULL) {
-		cil_log(CIL_INFO, " %s", lvlrange->high_str);
-	}
-	cil_log(CIL_INFO, " )");
-}
-
-void cil_tree_print_context(struct cil_context *context)
-{
-	cil_log(CIL_INFO, " (");
-	if (context->user != NULL) {
-		cil_log(CIL_INFO, " %s", context->user->datum.name);
-	} else if (context->user_str != NULL) {
-		cil_log(CIL_INFO, " %s", context->user_str);
-	}
-
-	if (context->role != NULL) {
-		cil_log(CIL_INFO, " %s", context->role->datum.name);
-	} else if (context->role_str != NULL) {
-		cil_log(CIL_INFO, " %s", context->role_str);
-	}
-
-	if (context->type != NULL) {
-		cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum *)context->type)->name);
-	} else if (context->type_str != NULL) {
-		cil_log(CIL_INFO, " %s", context->type_str);
-	}
-
-	if (context->range != NULL) {
-		cil_tree_print_levelrange(context->range);
-	} else if (context->range_str != NULL) {
-		cil_log(CIL_INFO, " %s", context->range_str);
-	}
-
-	cil_log(CIL_INFO, " )");
-
-	return;
-}
-
-void cil_tree_print_constrain(struct cil_constrain *cons)
-{
-	cil_tree_print_classperms_list(cons->classperms);
-
-	cil_tree_print_expr(cons->datum_expr, cons->str_expr);
-
-	cil_log(CIL_INFO, "\n");
-}
-
-void cil_tree_print_node(struct cil_tree_node *node)
-{
-	if (node->data == NULL) {
-		cil_log(CIL_INFO, "FLAVOR: %d", node->flavor);
-		return;
-	} else {
-		switch( node->flavor ) {
-		case CIL_BLOCK: {
-			struct cil_block *block = node->data;
-			cil_log(CIL_INFO, "BLOCK: %s\n", block->datum.name);
-			return;
-		}
-		case CIL_BLOCKINHERIT: {
-			struct cil_blockinherit *inherit = node->data;
-			cil_log(CIL_INFO, "BLOCKINHERIT: %s\n", inherit->block_str);
-			return;
-		}
-		case CIL_BLOCKABSTRACT: {
-			struct cil_blockabstract *abstract = node->data;
-			cil_log(CIL_INFO, "BLOCKABSTRACT: %s\n", abstract->block_str);
-			return;
-		}
-		case CIL_IN: {
-			struct cil_in *in = node->data;
-			cil_log(CIL_INFO, "IN: %s\n", in->block_str);
-			return;
-		}
-		case CIL_USER: {
-			struct cil_user *user = node->data;
-			cil_log(CIL_INFO, "USER: %s\n", user->datum.name);
-			return;
-		}
-		case CIL_TYPE: {
-			struct cil_type *type = node->data;
-			cil_log(CIL_INFO, "TYPE: %s\n", type->datum.name);
-			return;
-		}
-		case CIL_EXPANDTYPEATTRIBUTE: {
-			struct cil_expandtypeattribute *attr = node->data;
-
-			cil_log(CIL_INFO, "(EXPANDTYPEATTRIBUTE ");
-			cil_tree_print_expr(attr->attr_datums, attr->attr_strs);
-			cil_log(CIL_INFO, "%s)\n",attr->expand ?
-					CIL_KEY_CONDTRUE : CIL_KEY_CONDFALSE);
-
-			return;
-		}
-		case CIL_TYPEATTRIBUTESET: {
-			struct cil_typeattributeset *attr = node->data;
-
-			cil_log(CIL_INFO, "(TYPEATTRIBUTESET %s ", attr->attr_str);
-
-			cil_tree_print_expr(attr->datum_expr, attr->str_expr);
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_TYPEATTRIBUTE: {
-			struct cil_typeattribute *attr = node->data;
-			cil_log(CIL_INFO, "TYPEATTRIBUTE: %s\n", attr->datum.name);
-			return;
-		}
-		case CIL_ROLE: {
-			struct cil_role *role = node->data;
-			cil_log(CIL_INFO, "ROLE: %s\n", role->datum.name);
-			return;
-		}
-		case CIL_USERROLE: {
-			struct cil_userrole *userrole = node->data;
-			cil_log(CIL_INFO, "USERROLE:");
-			struct cil_symtab_datum *datum = NULL;
-
-			if (userrole->user != NULL) {
-				datum = userrole->user;
-				cil_log(CIL_INFO, " %s", datum->name);
-			} else if (userrole->user_str != NULL) {
-				cil_log(CIL_INFO, " %s", userrole->user_str);
-			}
-
-			if (userrole->role != NULL) {
-				datum = userrole->role;
-				cil_log(CIL_INFO, " %s", datum->name);
-			} else if (userrole->role_str != NULL) {
-				cil_log(CIL_INFO, " %s", userrole->role_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_USERLEVEL: {
-			struct cil_userlevel *usrlvl = node->data;
-			cil_log(CIL_INFO, "USERLEVEL:");
-
-			if (usrlvl->user_str != NULL) {
-				cil_log(CIL_INFO, " %s", usrlvl->user_str);
-			}
-
-			if (usrlvl->level != NULL) {
-				cil_log(CIL_INFO, " (");
-				cil_tree_print_level(usrlvl->level);
-				cil_log(CIL_INFO, " )");
-			} else if (usrlvl->level_str != NULL) {
-				cil_log(CIL_INFO, " %s", usrlvl->level_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_USERRANGE: {
-			struct cil_userrange *userrange = node->data;
-			cil_log(CIL_INFO, "USERRANGE:");
-
-			if (userrange->user_str != NULL) {
-				cil_log(CIL_INFO, " %s", userrange->user_str);
-			}
-
-			if (userrange->range != NULL) {
-				cil_log(CIL_INFO, " (");
-				cil_tree_print_levelrange(userrange->range);
-				cil_log(CIL_INFO, " )");
-			} else if (userrange->range_str != NULL) {
-				cil_log(CIL_INFO, " %s", userrange->range_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_USERBOUNDS: {
-			struct cil_bounds *bnds = node->data;
-			cil_log(CIL_INFO, "USERBOUNDS: user: %s, bounds: %s\n", bnds->parent_str, bnds->child_str);
-			return;
-		}
-		case CIL_ROLETYPE: {
-			struct cil_roletype *roletype = node->data;
-			struct cil_symtab_datum *datum = NULL;
-			cil_log(CIL_INFO, "ROLETYPE:");
-
-			if (roletype->role != NULL) {
-				datum = roletype->role;
-				cil_log(CIL_INFO, " %s", datum->name);
-			} else if (roletype->role_str != NULL) {
-				cil_log(CIL_INFO, " %s", roletype->role_str);
-			}
-
-			if (roletype->type != NULL) {
-				datum = roletype->type;
-				cil_log(CIL_INFO, " %s", datum->name);
-			} else if (roletype->type_str != NULL) {
-				cil_log(CIL_INFO, " %s", roletype->type_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_ROLETRANSITION: {
-			struct cil_roletransition *roletrans = node->data;
-			cil_log(CIL_INFO, "ROLETRANSITION:");
-
-			if (roletrans->src != NULL) {
-				cil_log(CIL_INFO, " %s", roletrans->src->datum.name);
-			} else {
-				cil_log(CIL_INFO, " %s", roletrans->src_str);
-			}
-
-			if (roletrans->tgt != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum *)roletrans->tgt)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", roletrans->tgt_str);
-			}
-				
-			if (roletrans->obj != NULL) {
-				cil_log(CIL_INFO, " %s", roletrans->obj->datum.name);
-			} else {
-				cil_log(CIL_INFO, " %s", roletrans->obj_str);
-			}
-
-			if (roletrans->result != NULL) {
-				cil_log(CIL_INFO, " %s\n", roletrans->result->datum.name);
-			} else {
-				cil_log(CIL_INFO, " %s\n", roletrans->result_str);
-			}
-
-			return;
-		}
-		case CIL_ROLEALLOW: {
-			struct cil_roleallow *roleallow = node->data;
-			cil_log(CIL_INFO, "ROLEALLOW:");
-
-			if (roleallow->src != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum*)roleallow->src)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", roleallow->src_str);
-			}
-
-			if (roleallow->tgt != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum*)roleallow->tgt)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", roleallow->tgt_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_ROLEATTRIBUTESET: {
-			struct cil_roleattributeset *attr = node->data;
-
-			cil_log(CIL_INFO, "(ROLEATTRIBUTESET %s ", attr->attr_str);
-
-			cil_tree_print_expr(attr->datum_expr, attr->str_expr);
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_ROLEATTRIBUTE: {
-			struct cil_roleattribute *attr = node->data;
-			cil_log(CIL_INFO, "ROLEATTRIBUTE: %s\n", attr->datum.name);
-			return;
-		}
-		case CIL_USERATTRIBUTESET: {
-			struct cil_userattributeset *attr = node->data;
-
-			cil_log(CIL_INFO, "(USERATTRIBUTESET %s ", attr->attr_str);
-
-			cil_tree_print_expr(attr->datum_expr, attr->str_expr);
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_USERATTRIBUTE: {
-			struct cil_userattribute *attr = node->data;
-			cil_log(CIL_INFO, "USERATTRIBUTE: %s\n", attr->datum.name);
-			return;
-		}
-		case CIL_ROLEBOUNDS: {
-			struct cil_bounds *bnds = node->data;
-			cil_log(CIL_INFO, "ROLEBOUNDS: role: %s, bounds: %s\n", bnds->parent_str, bnds->child_str);
-			return;
-		}
-		case CIL_CLASS: {
-			struct cil_class *cls = node->data;
-			cil_log(CIL_INFO, "CLASS: %s ", cls->datum.name);
-				
-			if (cls->common != NULL) {
-				cil_log(CIL_INFO, "inherits: %s ", cls->common->datum.name);
-			}
-			cil_log(CIL_INFO, "(");
-	
-			cil_tree_print_perms_list(node->cl_head);
-	
-			cil_log(CIL_INFO, " )");
-			return;
-		}
-		case CIL_CLASSORDER: {
-			struct cil_classorder *classorder = node->data;
-			struct cil_list_item *class;
-
-			if (classorder->class_list_str == NULL) {
-				cil_log(CIL_INFO, "CLASSORDER: ()\n");
-				return;
-			}
-
-			cil_log(CIL_INFO, "CLASSORDER: (");
-			cil_list_for_each(class, classorder->class_list_str) {
-				cil_log(CIL_INFO, " %s", (char*)class->data);
-			}
-			cil_log(CIL_INFO, " )\n");
-			return;
-		}
-		case CIL_COMMON: {
-			struct cil_class *common = node->data;
-			cil_log(CIL_INFO, "COMMON: %s (", common->datum.name);
-		
-			cil_tree_print_perms_list(node->cl_head);
-	
-			cil_log(CIL_INFO, " )");
-			return;
-		}
-		case CIL_CLASSCOMMON: {
-			struct cil_classcommon *clscom = node->data;
-
-			cil_log(CIL_INFO, "CLASSCOMMON: class: %s, common: %s\n", clscom->class_str, clscom->common_str);
-
-			return;
-		}
-		case CIL_CLASSPERMISSION: {
-			struct cil_classpermission *cp = node->data;
-
-			cil_log(CIL_INFO, "CLASSPERMISSION: %s", cp->datum.name);
-
-			cil_log(CIL_INFO, "\n");
-
-			return;
-		}
-		case CIL_CLASSPERMISSIONSET: {
-			struct cil_classpermissionset *cps = node->data;
-
-			cil_log(CIL_INFO, "CLASSPERMISSIONSET: %s", cps->set_str);
-
-			cil_tree_print_classperms_list(cps->classperms);
-
-			cil_log(CIL_INFO, "\n");
-
-			return;
-		}
-		case CIL_MAP_CLASS: {
-			struct cil_class *cm = node->data;
-			cil_log(CIL_INFO, "MAP_CLASS: %s", cm->datum.name);
-
-			cil_log(CIL_INFO, " (");
-			cil_tree_print_perms_list(node->cl_head);
-			cil_log(CIL_INFO, " )\n");
-
-			return;
-		}
-		case CIL_MAP_PERM: {
-			struct cil_perm *cmp = node->data;
-
-			cil_log(CIL_INFO, "MAP_PERM: %s", cmp->datum.name);
-
-			if (cmp->classperms == NULL) {
-				cil_log(CIL_INFO, " perms: ()");
-				return;
-			}
-
-			cil_log(CIL_INFO, " kernel class perms: (");
-
-			cil_tree_print_classperms_list(cmp->classperms);
-
-			cil_log(CIL_INFO, " )\n");
-
-			return;
-		}
-		case CIL_CLASSMAPPING: {
-			struct cil_classmapping *mapping = node->data;
-
-			cil_log(CIL_INFO, "CLASSMAPPING: map class: %s, map perm: %s,", mapping->map_class_str, mapping->map_perm_str);
-
-			cil_log(CIL_INFO, " (");
-
-			cil_tree_print_classperms_list(mapping->classperms);
-
-			cil_log(CIL_INFO, " )\n");
-			return;
-		}
-		case CIL_BOOL: {
-			struct cil_bool *boolean = node->data;
-			cil_log(CIL_INFO, "BOOL: %s, value: %d\n", boolean->datum.name, boolean->value);
-			return;
-		}
-		case CIL_TUNABLE: {
-			struct cil_tunable *tunable = node->data;
-			cil_log(CIL_INFO, "TUNABLE: %s, value: %d\n", tunable->datum.name, tunable->value);
-			return;
-		}
-		case CIL_BOOLEANIF: {
-			struct cil_booleanif *bif = node->data;
-
-			cil_log(CIL_INFO, "(BOOLEANIF ");
-
-			cil_tree_print_expr(bif->datum_expr, bif->str_expr);
-
-			cil_log(CIL_INFO, " )\n");
-			return;
-		}
-		case CIL_TUNABLEIF: {
-			struct cil_tunableif *tif = node->data;
-
-			cil_log(CIL_INFO, "(TUNABLEIF ");
-
-			cil_tree_print_expr(tif->datum_expr, tif->str_expr);
-
-			cil_log(CIL_INFO, " )\n");
-			return;
-		}
-		case CIL_CONDBLOCK: {
-			struct cil_condblock *cb = node->data;
-			if (cb->flavor == CIL_CONDTRUE) {
-				cil_log(CIL_INFO, "true\n");
-			} else if (cb->flavor == CIL_CONDFALSE) {
-				cil_log(CIL_INFO, "false\n");
-			}
-			return;
-		}
-		case CIL_ALL:
-			cil_log(CIL_INFO, "all");
-			return;
-		case CIL_AND:
-			cil_log(CIL_INFO, "&&");
-			return;
-		case CIL_OR:
-			cil_log(CIL_INFO, "|| ");
-			return;
-		case CIL_NOT:
-			cil_log(CIL_INFO, "!");
-			return;
-		case CIL_EQ:
-			cil_log(CIL_INFO, "==");
-			return;
-		case CIL_NEQ:
-			cil_log(CIL_INFO, "!=");
-			return;
-		case CIL_TYPEALIAS: {
-			struct cil_alias *alias = node->data;
-			cil_log(CIL_INFO, "TYPEALIAS: %s\n", alias->datum.name);
-			return;
-		}
-		case CIL_TYPEALIASACTUAL: {
-			struct cil_aliasactual *aliasactual = node->data;
-			cil_log(CIL_INFO, "TYPEALIASACTUAL: type: %s, alias: %s\n", aliasactual->alias_str, aliasactual->actual_str);
-			return;
-		}
-		case CIL_TYPEBOUNDS: {
-			struct cil_bounds *bnds = node->data;
-			cil_log(CIL_INFO, "TYPEBOUNDS: type: %s, bounds: %s\n", bnds->parent_str, bnds->child_str);
-			return;
-		}
-		case CIL_TYPEPERMISSIVE: {
-			struct cil_typepermissive *typeperm = node->data;
-
-			if (typeperm->type != NULL) {
-				cil_log(CIL_INFO, "TYPEPERMISSIVE: %s\n", ((struct cil_symtab_datum *)typeperm->type)->name);
-			} else {
-				cil_log(CIL_INFO, "TYPEPERMISSIVE: %s\n", typeperm->type_str);
-			}
-
-			return;
-		}
-		case CIL_NAMETYPETRANSITION: {
-			struct cil_nametypetransition *nametypetrans = node->data;
-			cil_log(CIL_INFO, "TYPETRANSITION:");
-
-			if (nametypetrans->src != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum *)nametypetrans->src)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", nametypetrans->src_str);
-			}
-
-			if (nametypetrans->tgt != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum *)nametypetrans->tgt)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", nametypetrans->tgt_str);
-			}
-
-			if (nametypetrans->obj != NULL) {
-				cil_log(CIL_INFO, " %s", nametypetrans->obj->datum.name);
-			} else {
-				cil_log(CIL_INFO, " %s", nametypetrans->obj_str);
-			}
-
-			cil_log(CIL_INFO, " %s\n", nametypetrans->name_str);
-
-			if (nametypetrans->result != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum *)nametypetrans->result)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", nametypetrans->result_str);
-			}
-
-			return;
-		}
-		case CIL_RANGETRANSITION: {
-			struct cil_rangetransition *rangetrans = node->data;
-			cil_log(CIL_INFO, "RANGETRANSITION:");
-
-			if (rangetrans->src != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum *)rangetrans->src)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", rangetrans->src_str);
-			}
-
-			if (rangetrans->exec != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum *)rangetrans->exec)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", rangetrans->exec_str);
-			}
-
-			if (rangetrans->obj != NULL) {
-				cil_log(CIL_INFO, " %s", rangetrans->obj->datum.name);
-			} else {
-				cil_log(CIL_INFO, " %s", rangetrans->obj_str);
-			}
-
-			if (rangetrans->range != NULL) {
-				cil_log(CIL_INFO, " (");
-				cil_tree_print_levelrange(rangetrans->range);
-				cil_log(CIL_INFO, " )");
-			} else {
-				cil_log(CIL_INFO, " %s", rangetrans->range_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_AVRULE: {
-			struct cil_avrule *rule = node->data;
-			switch (rule->rule_kind) {
-			case CIL_AVRULE_ALLOWED:
-				cil_log(CIL_INFO, "ALLOW:");
-				break;
-			case CIL_AVRULE_AUDITALLOW:
-				cil_log(CIL_INFO, "AUDITALLOW:");
-				break;
-			case CIL_AVRULE_DONTAUDIT:
-				cil_log(CIL_INFO, "DONTAUDIT:");
-				break;
-			case CIL_AVRULE_NEVERALLOW:
-				cil_log(CIL_INFO, "NEVERALLOW:");
-				break;
-			}
-
-			if (rule->src != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum*)rule->src)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", rule->src_str);
-			}
-
-			if (rule->tgt != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum*)rule->tgt)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", rule->tgt_str);
-			}
-
-			cil_tree_print_classperms_list(rule->perms.classperms);
-
-			cil_log(CIL_INFO, "\n");
-
-			return;
-		}
-		case CIL_TYPE_RULE: {
-			struct cil_type_rule *rule = node->data;
-			switch (rule->rule_kind) {
-			case CIL_TYPE_TRANSITION:
-				cil_log(CIL_INFO, "TYPETRANSITION:");
-				break;
-			case CIL_TYPE_MEMBER:
-				cil_log(CIL_INFO, "TYPEMEMBER:");
-				break;
-			case CIL_TYPE_CHANGE:
-				cil_log(CIL_INFO, "TYPECHANGE:");
-				break;
-			}
-
-			if (rule->src != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum *)rule->src)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", rule->src_str);
-			}
-
-			if (rule->tgt != NULL) {
-				cil_log(CIL_INFO, " %s", ((struct cil_symtab_datum *)rule->tgt)->name);
-			} else {
-				cil_log(CIL_INFO, " %s", rule->tgt_str);
-			}
-
-			if (rule->obj != NULL) {
-				cil_log(CIL_INFO, " %s", rule->obj->datum.name);
-			} else {
-				cil_log(CIL_INFO, " %s", rule->obj_str);
-			}
-
-			if (rule->result != NULL) {
-				cil_log(CIL_INFO, " %s\n", ((struct cil_symtab_datum *)rule->result)->name);
-			} else {
-				cil_log(CIL_INFO, " %s\n", rule->result_str);
-			}
-
-			return;
-		}
-		case CIL_SENS: {
-			struct cil_sens *sens = node->data;
-			cil_log(CIL_INFO, "SENSITIVITY: %s\n", sens->datum.name);
-			return;
-		}
-		case CIL_SENSALIAS: {
-			struct cil_alias *alias = node->data;
-			cil_log(CIL_INFO, "SENSITIVITYALIAS: %s\n", alias->datum.name);
-			return;
-		}
-		case CIL_SENSALIASACTUAL: {
-			struct cil_aliasactual *aliasactual = node->data;
-			cil_log(CIL_INFO, "SENSITIVITYALIAS: alias: %s, sensitivity: %s\n", aliasactual->alias_str, aliasactual->actual_str);
-
-			return;
-		}
-		case CIL_CAT: {
-			struct cil_cat *cat = node->data;
-			cil_log(CIL_INFO, "CATEGORY: %s\n", cat->datum.name);
-			return;
-		}
-		case CIL_CATALIAS: {
-			struct cil_alias *alias = node->data;
-			cil_log(CIL_INFO, "CATEGORYALIAS: %s\n", alias->datum.name);
-			return;
-		}
-		case CIL_CATALIASACTUAL: {
-			struct cil_aliasactual *aliasactual = node->data;
-			cil_log(CIL_INFO, "CATEGORYALIAS: alias %s, category: %s\n", aliasactual->alias_str, aliasactual->actual_str);
-			return;
-		}
-		case CIL_CATSET: {
-			struct cil_catset *catset = node->data;
-
-			cil_log(CIL_INFO, "CATSET: %s ",catset->datum.name);
-
-			cil_tree_print_cats(catset->cats);
-
-			return;
-		}
-		case CIL_CATORDER: {
-			struct cil_catorder *catorder = node->data;
-			struct cil_list_item *cat;
-
-			if (catorder->cat_list_str == NULL) {
-				cil_log(CIL_INFO, "CATORDER: ()\n");
-				return;
-			}
-
-			cil_log(CIL_INFO, "CATORDER: (");
-			cil_list_for_each(cat, catorder->cat_list_str) {
-				cil_log(CIL_INFO, " %s", (char*)cat->data);
-			}
-			cil_log(CIL_INFO, " )\n");
-			return;
-		}
-		case CIL_SENSCAT: {
-			struct cil_senscat *senscat = node->data;
-
-			cil_log(CIL_INFO, "SENSCAT: sens:");
-
-			if (senscat->sens_str != NULL) {
-				cil_log(CIL_INFO, " %s ", senscat->sens_str);
-			} else {
-				cil_log(CIL_INFO, " [processed]");
-			}
-
-			cil_tree_print_cats(senscat->cats);
-
-			return;
-		}
-		case CIL_SENSITIVITYORDER: {
-			struct cil_sensorder *sensorder = node->data;
-			struct cil_list_item *sens;
-
-			cil_log(CIL_INFO, "SENSITIVITYORDER: (");
-
-			if (sensorder->sens_list_str != NULL) {
-				cil_list_for_each(sens, sensorder->sens_list_str) {
-					if (sens->flavor == CIL_LIST) {
-						struct cil_list_item *sub;
-						cil_log(CIL_INFO, " (");
-						cil_list_for_each(sub, (struct cil_list*)sens->data) {
-							cil_log(CIL_INFO, " %s", (char*)sub->data);
-						}
-						cil_log(CIL_INFO, " )");
-					} else {
-						cil_log(CIL_INFO, " %s", (char*)sens->data);
-					}
-				}
-			}
-
-			cil_log(CIL_INFO, " )\n");
-			return;
-		}
-		case CIL_LEVEL: {
-			struct cil_level *level = node->data;
-			cil_log(CIL_INFO, "LEVEL %s:", level->datum.name); 
-			cil_tree_print_level(level);
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_LEVELRANGE: {
-			struct cil_levelrange *lvlrange = node->data;
-			cil_log(CIL_INFO, "LEVELRANGE %s:", lvlrange->datum.name);
-			cil_tree_print_levelrange(lvlrange);
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_CONSTRAIN: {
-			struct cil_constrain *cons = node->data;
-			cil_log(CIL_INFO, "CONSTRAIN: (");
-			cil_tree_print_constrain(cons);
-			return;
-		}
-		case CIL_MLSCONSTRAIN: {
-			struct cil_constrain *cons = node->data;
-			cil_log(CIL_INFO, "MLSCONSTRAIN: (");
-			cil_tree_print_constrain(cons);
-			return;
-		}
-		case CIL_VALIDATETRANS: {
-			struct cil_validatetrans *vt = node->data;
-
-			cil_log(CIL_INFO, "(VALIDATETRANS ");
-
-			if (vt->class != NULL) {
-				cil_log(CIL_INFO, "%s ", vt->class->datum.name);
-			} else if (vt->class_str != NULL) {
-				cil_log(CIL_INFO, "%s ", vt->class_str);
-			}
-
-			cil_tree_print_expr(vt->datum_expr, vt->str_expr);
-
-			cil_log(CIL_INFO, ")\n");
-			return;
-		}
-		case CIL_MLSVALIDATETRANS: {
-			struct cil_validatetrans *vt = node->data;
-
-			cil_log(CIL_INFO, "(MLSVALIDATETRANS ");
-
-			if (vt->class != NULL) {
-				cil_log(CIL_INFO, "%s ", vt->class->datum.name);
-			} else if (vt->class_str != NULL) {
-				cil_log(CIL_INFO, "%s ", vt->class_str);
-			}
-
-			cil_tree_print_expr(vt->datum_expr, vt->str_expr);
-
-			cil_log(CIL_INFO, ")\n");
-			return;
-		}
-		case CIL_CONTEXT: {
-			struct cil_context *context = node->data;
-			cil_log(CIL_INFO, "CONTEXT %s:", context->datum.name);
-			cil_tree_print_context(context);
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_FILECON: {
-			struct cil_filecon *filecon = node->data;
-			cil_log(CIL_INFO, "FILECON:");
-			cil_log(CIL_INFO, " %s %d", filecon->path_str, filecon->type);
-
-			if (filecon->context != NULL) {
-				cil_tree_print_context(filecon->context);
-			} else if (filecon->context_str != NULL) {
-				cil_log(CIL_INFO, " %s", filecon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-
-		}
-		case CIL_IBPKEYCON: {
-			struct cil_ibpkeycon *ibpkeycon = node->data;
-
-			cil_log(CIL_INFO, "IBPKEYCON: %s", ibpkeycon->subnet_prefix_str);
-			cil_log(CIL_INFO, " (%d %d) ", ibpkeycon->pkey_low, ibpkeycon->pkey_high);
-
-			if (ibpkeycon->context)
-				cil_tree_print_context(ibpkeycon->context);
-			else if (ibpkeycon->context_str)
-				cil_log(CIL_INFO, " %s", ibpkeycon->context_str);
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_PORTCON: {
-			struct cil_portcon *portcon = node->data;
-			cil_log(CIL_INFO, "PORTCON:");
-			if (portcon->proto == CIL_PROTOCOL_UDP) {
-				cil_log(CIL_INFO, " udp");
-			} else if (portcon->proto == CIL_PROTOCOL_TCP) {
-				cil_log(CIL_INFO, " tcp");
-			} else if (portcon->proto == CIL_PROTOCOL_DCCP) {
-				cil_log(CIL_INFO, " dccp");
-			} else if (portcon->proto == CIL_PROTOCOL_SCTP) {
-				cil_log(CIL_INFO, " sctp");
-			}
-			cil_log(CIL_INFO, " (%d %d)", portcon->port_low, portcon->port_high);
-
-			if (portcon->context != NULL) {
-				cil_tree_print_context(portcon->context);
-			} else if (portcon->context_str != NULL) {
-				cil_log(CIL_INFO, " %s", portcon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_NODECON: {
-			struct cil_nodecon *nodecon = node->data;
-			char buf[256];
-				
-			cil_log(CIL_INFO, "NODECON:");
-				
-			if (nodecon->addr) {
-				inet_ntop(nodecon->addr->family, &nodecon->addr->ip, buf, 256);
-				cil_log(CIL_INFO, " %s", buf);
-			}  else {
-				cil_log(CIL_INFO, " %s", nodecon->addr_str);
-			}
-
-			if (nodecon->mask) {
-				inet_ntop(nodecon->mask->family, &nodecon->mask->ip, buf, 256);
-				cil_log(CIL_INFO, " %s", buf);
-			} else {
-				cil_log(CIL_INFO, " %s", nodecon->mask_str);
-			}
-				
-			if (nodecon->context != NULL) {
-				cil_tree_print_context(nodecon->context);
-			} else if (nodecon->context_str != NULL) {
-				cil_log(CIL_INFO, " %s", nodecon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_GENFSCON: {
-			struct cil_genfscon *genfscon = node->data;
-			cil_log(CIL_INFO, "GENFSCON:");
-			cil_log(CIL_INFO, " %s %s", genfscon->fs_str, genfscon->path_str);
-
-			if (genfscon->context != NULL) {
-				cil_tree_print_context(genfscon->context);
-			} else if (genfscon->context_str != NULL) {
-				cil_log(CIL_INFO, " %s", genfscon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_NETIFCON: {
-			struct cil_netifcon *netifcon = node->data;
-			cil_log(CIL_INFO, "NETIFCON %s", netifcon->interface_str);
-
-			if (netifcon->if_context != NULL) {
-				cil_tree_print_context(netifcon->if_context);
-			} else if (netifcon->if_context_str != NULL) {
-				cil_log(CIL_INFO, " %s", netifcon->if_context_str);
-			}
-
-			if (netifcon->packet_context != NULL) {
-				cil_tree_print_context(netifcon->packet_context);
-			} else if (netifcon->packet_context_str != NULL) {
-				cil_log(CIL_INFO, " %s", netifcon->packet_context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_IBENDPORTCON: {
-			struct cil_ibendportcon *ibendportcon = node->data;
-
-			cil_log(CIL_INFO, "IBENDPORTCON: %s %u ", ibendportcon->dev_name_str, ibendportcon->port);
-
-			if (ibendportcon->context)
-				cil_tree_print_context(ibendportcon->context);
-			else if (ibendportcon->context_str)
-				cil_log(CIL_INFO, " %s", ibendportcon->context_str);
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_PIRQCON: {
-			struct cil_pirqcon *pirqcon = node->data;
-
-			cil_log(CIL_INFO, "PIRQCON %d", pirqcon->pirq);
-			if (pirqcon->context != NULL) {
-				cil_tree_print_context(pirqcon->context);
-			} else {
-				cil_log(CIL_INFO, " %s", pirqcon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_IOMEMCON: {
-			struct cil_iomemcon *iomemcon = node->data;
-
-			cil_log(CIL_INFO, "IOMEMCON ( %"PRId64" %"PRId64" )", iomemcon->iomem_low, iomemcon->iomem_high);
-			if (iomemcon->context != NULL) {
-				cil_tree_print_context(iomemcon->context);
-			} else {
-				cil_log(CIL_INFO, " %s", iomemcon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_IOPORTCON: {
-			struct cil_ioportcon *ioportcon = node->data;
-
-			cil_log(CIL_INFO, "IOPORTCON ( %d %d )", ioportcon->ioport_low, ioportcon->ioport_high);
-			if (ioportcon->context != NULL) {
-				cil_tree_print_context(ioportcon->context);
-			} else {
-				cil_log(CIL_INFO, " %s", ioportcon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_PCIDEVICECON: {
-			struct cil_pcidevicecon *pcidevicecon = node->data;
-
-			cil_log(CIL_INFO, "PCIDEVICECON %d", pcidevicecon->dev);
-			if (pcidevicecon->context != NULL) {
-				cil_tree_print_context(pcidevicecon->context);
-			} else {
-				cil_log(CIL_INFO, " %s", pcidevicecon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_DEVICETREECON: {
-			struct cil_devicetreecon *devicetreecon = node->data;
-
-			cil_log(CIL_INFO, "DEVICETREECON %s", devicetreecon->path);
-			if (devicetreecon->context != NULL) {
-				cil_tree_print_context(devicetreecon->context);
-			} else {
-				cil_log(CIL_INFO, " %s", devicetreecon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_FSUSE: {
-			struct cil_fsuse *fsuse = node->data;
-			cil_log(CIL_INFO, "FSUSE: ");
-
-			if (fsuse->type == CIL_FSUSE_XATTR) {
-				cil_log(CIL_INFO, "xattr ");
-			} else if (fsuse->type == CIL_FSUSE_TASK) {
-				cil_log(CIL_INFO, "task ");
-			} else if (fsuse->type == CIL_FSUSE_TRANS) {
-				cil_log(CIL_INFO, "trans ");
-			} else {
-				cil_log(CIL_INFO, "unknown ");
-			}
-
-			cil_log(CIL_INFO, "%s ", fsuse->fs_str);
-
-			if (fsuse->context != NULL) {
-				cil_tree_print_context(fsuse->context);
-			} else {
-				cil_log(CIL_INFO, " %s", fsuse->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_SID: {
-			struct cil_sid *sid = node->data;
-			cil_log(CIL_INFO, "SID: %s\n", sid->datum.name);
-			return;
-		}
-		case CIL_SIDCONTEXT: {
-			struct cil_sidcontext *sidcon = node->data;
-			cil_log(CIL_INFO, "SIDCONTEXT: %s", sidcon->sid_str);
-
-			if (sidcon->context != NULL) {
-				cil_tree_print_context(sidcon->context);
-			} else {
-				cil_log(CIL_INFO, " %s", sidcon->context_str);
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}
-		case CIL_SIDORDER: {
-			struct cil_sidorder *sidorder = node->data;
-			struct cil_list_item *sid;
-
-			if (sidorder->sid_list_str == NULL) {
-				cil_log(CIL_INFO, "SIDORDER: ()\n");
-				return;
-			}
-
-			cil_log(CIL_INFO, "SIDORDER: (");
-			cil_list_for_each(sid, sidorder->sid_list_str) {
-				cil_log(CIL_INFO, " %s", (char*)sid->data);
-			}
-			cil_log(CIL_INFO, " )\n");
-			return;
-		}
-		case CIL_POLICYCAP: {
-			struct cil_policycap *polcap = node->data;
-			cil_log(CIL_INFO, "POLICYCAP: %s\n", polcap->datum.name);
-			return;
-		}
-		case CIL_MACRO: {
-			struct cil_macro *macro = node->data;
-			cil_log(CIL_INFO, "MACRO %s:", macro->datum.name);
-
-			if (macro->params != NULL && macro->params->head != NULL) {
-				struct cil_list_item *curr_param;
-				cil_log(CIL_INFO, " parameters: (");
-				cil_list_for_each(curr_param, macro->params) {
-					cil_log(CIL_INFO, " flavor: %d, string: %s;", ((struct cil_param*)curr_param->data)->flavor, ((struct cil_param*)curr_param->data)->str);
-
-				}
-				cil_log(CIL_INFO, " )");
-			}
-			cil_log(CIL_INFO, "\n");
-
-			return;
-		}
-		case CIL_CALL: {
-			struct cil_call *call = node->data;
-			cil_log(CIL_INFO, "CALL: macro name:");
-
-			if (call->macro != NULL) {
-				cil_log(CIL_INFO, " %s", call->macro->datum.name);
-			} else {
-				cil_log(CIL_INFO, " %s", call->macro_str);
-			}
-
-			if (call->args != NULL) {
-				cil_log(CIL_INFO, ", args: ( ");
-				struct cil_list_item *item;
-				cil_list_for_each(item, call->args) {
-					struct cil_symtab_datum *datum = ((struct cil_args*)item->data)->arg;
-					if (datum != NULL) {
-						if (datum->nodes != NULL && datum->nodes->head != NULL) {
-							cil_tree_print_node(NODE(datum));
-						}
-					} else if (((struct cil_args*)item->data)->arg_str != NULL) {
-						switch (item->flavor) {
-						case CIL_TYPE: cil_log(CIL_INFO, "type:"); break;
-						case CIL_USER: cil_log(CIL_INFO, "user:"); break;
-						case CIL_ROLE: cil_log(CIL_INFO, "role:"); break;
-						case CIL_SENS: cil_log(CIL_INFO, "sensitivity:"); break;
-						case CIL_CAT: cil_log(CIL_INFO, "category:"); break;
-						case CIL_CATSET: cil_log(CIL_INFO, "categoryset:"); break;
-						case CIL_LEVEL: cil_log(CIL_INFO, "level:"); break;
-						case CIL_CLASS: cil_log(CIL_INFO, "class:"); break;
-						default: break;
-						}
-						cil_log(CIL_INFO, "%s ", ((struct cil_args*)item->data)->arg_str);
-					}
-				}
-				cil_log(CIL_INFO, ")");
-			}
-
-			cil_log(CIL_INFO, "\n");
-			return;
-		}	
-		case CIL_OPTIONAL: {
-			struct cil_optional *optional = node->data;
-			cil_log(CIL_INFO, "OPTIONAL: %s\n", optional->datum.name);
-			return;
-		}
-		case CIL_IPADDR: {
-			struct cil_ipaddr *ipaddr = node->data;
-			char buf[256];
-
-			inet_ntop(ipaddr->family, &ipaddr->ip, buf, 256);
-			cil_log(CIL_INFO, "IPADDR %s: %s\n", ipaddr->datum.name, buf);
-
-			break;
-		}
-		default : {
-			cil_log(CIL_INFO, "CIL FLAVOR: %d\n", node->flavor);
-			return;
-		}
-		}
-	}
-}
-
-void cil_tree_print(struct cil_tree_node *tree, uint32_t depth)
-{
-	struct cil_tree_node *current = NULL;
-	current = tree;
-	uint32_t x = 0;
-
-	if (current != NULL) {
-		if (current->cl_head == NULL) {
-			if (current->flavor == CIL_NODE) {
-				if (current->parent->cl_head == current) {
-					cil_log(CIL_INFO, "%s", (char*)current->data);
-				} else {
-					cil_log(CIL_INFO, " %s", (char*)current->data);
-				}
-			} else if (current->flavor != CIL_PERM) {
-				for (x = 0; x<depth; x++) {
-					cil_log(CIL_INFO, "\t");
-				}
-				cil_tree_print_node(current);
-			}
-		} else {
-			if (current->parent != NULL) {
-				cil_log(CIL_INFO, "\n");
-				for (x = 0; x<depth; x++) {
-					cil_log(CIL_INFO, "\t");
-				}
-				cil_log(CIL_INFO, "(");
-
-				if (current->flavor != CIL_NODE) {
-					cil_tree_print_node(current);
-				}
-			}
-			cil_tree_print(current->cl_head, depth + 1);
-		}
-
-		if (current->next == NULL) {
-			if ((current->parent != NULL) && (current->parent->cl_tail == current) && (current->parent->parent != NULL)) {
-				if (current->flavor == CIL_PERM) {
-					cil_log(CIL_INFO, ")\n");
-				} else if (current->flavor != CIL_NODE) {
-					for (x = 0; x<depth-1; x++) {
-						cil_log(CIL_INFO, "\t");
-					}
-					cil_log(CIL_INFO, ")\n");
-				} else {
-					cil_log(CIL_INFO, ")");
-				}
-			}
-
-			if ((current->parent != NULL) && (current->parent->parent == NULL))
-				cil_log(CIL_INFO, "\n\n");
-		} else {
-			cil_tree_print(current->next, depth);
-		}
-	} else {
-		cil_log(CIL_INFO, "Tree is NULL\n");
-	}
-}
diff --git a/libsepol/cil/src/cil_tree.h b/libsepol/cil/src/cil_tree.h
index aeded5608239..bac9f1e47f2c 100644
--- a/libsepol/cil/src/cil_tree.h
+++ b/libsepol/cil/src/cil_tree.h
@@ -62,8 +62,6 @@ void cil_tree_children_destroy(struct cil_tree_node *node);
 void cil_tree_node_init(struct cil_tree_node **node);
 void cil_tree_node_destroy(struct cil_tree_node **node);
 
-void cil_tree_print(struct cil_tree_node *tree, uint32_t depth);
-
 //finished values
 #define CIL_TREE_SKIP_NOTHING	0
 #define CIL_TREE_SKIP_NEXT	1
diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c
new file mode 100644
index 000000000000..4871f7045e19
--- /dev/null
+++ b/libsepol/cil/src/cil_write_ast.c
@@ -0,0 +1,1573 @@
+/*
+ * Copyright 2011 Tresys Technology, LLC. 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,
+ *       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 TRESYS TECHNOLOGY, LLC ``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 TRESYS TECHNOLOGY, LLC OR CONTRIBUTORS 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.
+ *
+ * The views and conclusions contained in the software and documentation are those
+ * of the authors and should not be interpreted as representing official policies,
+ * either expressed or implied, of Tresys Technology, LLC.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#include "cil_internal.h"
+#include "cil_flavor.h"
+#include "cil_list.h"
+#include "cil_log.h"
+#include "cil_symtab.h"
+#include "cil_tree.h"
+#include "cil_write_ast.h"
+
+
+static inline const char *datum_or_str(struct cil_symtab_datum *datum, const char *str)
+{
+	return datum ? datum->fqn : str;
+}
+
+static inline const char *datum_to_str(struct cil_symtab_datum *datum)
+{
+	return datum ? datum->fqn : "<?DATUM>";
+}
+
+static void write_expr(FILE *out, struct cil_list *expr)
+{
+	struct cil_list_item *curr;
+	int notfirst = 0;
+
+	fprintf(out, "(");
+	cil_list_for_each(curr, expr) {
+		if (notfirst)
+			fprintf(out, " ");
+		else
+			notfirst = 1;
+		switch (curr->flavor) {
+		case CIL_LIST:
+			write_expr(out, curr->data);
+			break;
+		case CIL_STRING:
+			fprintf(out, "%s", (char *)curr->data);
+			break;
+		case CIL_DATUM:
+		case CIL_TYPE:
+		case CIL_ROLE:
+		case CIL_USER:
+		case CIL_SENS:
+		case CIL_CAT:
+		case CIL_BOOL:
+		case CIL_CLASS:
+		case CIL_MAP_CLASS:
+		case CIL_NAME:
+			fprintf(out, "%s", datum_to_str(curr->data));
+			break;
+		case CIL_OP: {
+			const char *op_str;
+			enum cil_flavor op_flavor = (enum cil_flavor)curr->data;
+			switch (op_flavor) {
+			case CIL_AND:
+				op_str = CIL_KEY_AND;
+				break;
+			case CIL_OR:
+				op_str = CIL_KEY_OR;
+				break;
+			case CIL_NOT:
+				op_str = CIL_KEY_NOT;
+				break;
+			case CIL_ALL:
+				op_str = CIL_KEY_ALL;
+				break;
+			case CIL_EQ:
+				op_str = CIL_KEY_EQ;
+				break;
+			case CIL_NEQ:
+				op_str = CIL_KEY_NEQ;
+				break;
+			case CIL_XOR:
+				op_str = CIL_KEY_XOR;
+				break;
+			case CIL_RANGE:
+				op_str = CIL_KEY_RANGE;
+				break;
+			case CIL_CONS_DOM:
+				op_str = CIL_KEY_CONS_DOM;
+				break;
+			case CIL_CONS_DOMBY:
+				op_str = CIL_KEY_CONS_DOMBY;
+				break;
+			case CIL_CONS_INCOMP:
+				op_str = CIL_KEY_CONS_INCOMP;
+				break;
+			default:
+				op_str = "<?OP>";
+				break;
+			}
+			fprintf(out, "%s", op_str);
+			break;
+		}
+		case CIL_CONS_OPERAND: {
+			const char *operand_str;
+			enum cil_flavor operand_flavor = (enum cil_flavor)curr->data;
+			switch (operand_flavor) {
+			case CIL_CONS_U1:
+				operand_str = CIL_KEY_CONS_U1;
+				break;
+			case CIL_CONS_U2:
+				operand_str = CIL_KEY_CONS_U2;
+				break;
+			case CIL_CONS_U3:
+				operand_str = CIL_KEY_CONS_U3;
+				break;
+			case CIL_CONS_T1:
+				operand_str = CIL_KEY_CONS_T1;
+				break;
+			case CIL_CONS_T2:
+				operand_str = CIL_KEY_CONS_T2;
+				break;
+			case CIL_CONS_T3:
+				operand_str = CIL_KEY_CONS_T3;
+				break;
+			case CIL_CONS_R1:
+				operand_str = CIL_KEY_CONS_R1;
+				break;
+			case CIL_CONS_R2:
+				operand_str = CIL_KEY_CONS_R2;
+				break;
+			case CIL_CONS_R3:
+				operand_str = CIL_KEY_CONS_R3;
+				break;
+			case CIL_CONS_L1:
+				operand_str = CIL_KEY_CONS_L1;
+				break;
+			case CIL_CONS_L2:
+				operand_str = CIL_KEY_CONS_L2;
+				break;
+			case CIL_CONS_H1:
+				operand_str = CIL_KEY_CONS_H1;
+				break;
+			case CIL_CONS_H2:
+				operand_str = CIL_KEY_CONS_H2;
+				break;
+			default:
+				operand_str = "<?OPERAND>";
+				break;
+			}
+			fprintf(out, "%s", operand_str);
+			break;
+		}
+		default:
+			fprintf(out, "<?FLAVOR>");
+			break;
+		}
+	}
+	fprintf(out, ")");
+}
+
+static void write_node_list(FILE *out, struct cil_tree_node *current)
+{
+	int notfirst = 0;
+
+	fprintf(out, "(");
+	while (current) {
+		if (notfirst)
+			fprintf(out, " ");
+		else
+			notfirst = 1;
+
+		fprintf(out, "%s", datum_to_str(current->data));
+		current = current->next;
+	}
+	fprintf(out, ")");
+}
+
+static void write_string_list(FILE *out, struct cil_list *list)
+{
+	struct cil_list_item *curr;
+	int notfirst = 0;
+
+	if (!list) {
+		fprintf(out, "()");
+		return;
+	}
+
+	fprintf(out, "(");
+	cil_list_for_each(curr, list) {
+		if (notfirst)
+			fprintf(out, " ");
+		else
+			notfirst = 1;
+		fprintf(out, "%s", (char*)curr->data);
+	}
+	fprintf(out, ")");
+}
+
+static void write_datum_list(FILE *out, struct cil_list *list)
+{
+	struct cil_list_item *curr;
+	int notfirst = 0;
+
+	if (!list) {
+		fprintf(out, "()");
+		return;
+	}
+
+	fprintf(out, "(");
+	cil_list_for_each(curr, list) {
+		if (notfirst)
+			fprintf(out, " ");
+		else
+			notfirst = 1;
+		fprintf(out, "%s", datum_to_str(curr->data));
+	}
+	fprintf(out, ")");
+}
+
+static void write_classperms(FILE *out, struct cil_classperms *cp)
+{
+	if (!cp) {
+		fprintf(out, "()");
+		return;
+	}
+
+	fprintf(out, "(%s ", datum_or_str(DATUM(cp->class), cp->class_str));
+	if (cp->perms)
+		write_expr(out, cp->perms);
+	else
+		write_expr(out, cp->perm_strs);
+	fprintf(out, ")");
+}
+
+static void write_classperms_list(FILE *out, struct cil_list *cp_list)
+{
+	struct cil_list_item *curr;
+	int notfirst = 0;
+	int num = 0;
+
+	if (!cp_list) {
+		fprintf(out, "()");
+		return;
+	}
+
+	cil_list_for_each(curr, cp_list) {
+		num++;
+	}
+	if (num > 1)
+		fprintf(out, "(");
+	cil_list_for_each(curr, cp_list) {
+		if (notfirst)
+			fprintf(out, " ");
+		else
+			notfirst = 1;
+		if (curr->flavor == CIL_CLASSPERMS) {
+			write_classperms(out, curr->data);
+		} else {
+			struct cil_classperms_set *cp_set = curr->data;
+			struct cil_classpermission *cp = cp_set->set;
+			if (cp) {
+				if (cp->datum.name)
+					fprintf(out, "%s", datum_to_str(DATUM(cp)));
+				else
+					write_classperms_list(out,cp->classperms);
+			} else {
+				fprintf(out, "%s", cp_set->set_str);
+			}
+		}
+	}
+	if (num > 1)
+		fprintf(out, ")");
+}
+
+static void write_permx(FILE *out, struct cil_permissionx *permx)
+{
+	if (permx->datum.name) {
+		fprintf(out, "%s", datum_to_str(DATUM(permx)));
+	} else {
+		fprintf(out, "(");
+		fprintf(out, "%s ", permx->kind == CIL_PERMX_KIND_IOCTL ? "ioctl" : "<?KIND>");
+		fprintf(out, "%s ", datum_or_str(DATUM(permx->obj), permx->obj_str));
+		write_expr(out, permx->expr_str);
+		fprintf(out, ")");
+	}
+}
+
+static void write_cats(FILE *out, struct cil_cats *cats)
+{
+	if (cats->datum_expr) {
+		write_expr(out, cats->datum_expr);
+	} else {
+		write_expr(out, cats->str_expr);
+	}
+}
+
+static void write_level(FILE *out, struct cil_level *level, int print_name)
+{
+	if (print_name && level->datum.name) {
+		fprintf(out, "%s", datum_to_str(DATUM(level)));
+	} else {
+		fprintf(out, "(");
+		fprintf(out, "%s", datum_or_str(DATUM(level->sens), level->sens_str));
+		if (level->cats) {
+			fprintf(out, " ");
+			write_cats(out, level->cats);
+		}
+		fprintf(out, ")");
+	}
+}
+
+static void write_range(FILE *out, struct cil_levelrange *range, int print_name)
+{
+	if (print_name && range->datum.name) {
+		fprintf(out, "%s", datum_to_str(DATUM(range)));
+	} else {
+		fprintf(out, "(");
+		if (range->low)
+			write_level(out, range->low, CIL_TRUE);
+		else
+			fprintf(out, "%s", range->low_str);
+		fprintf(out, " ");
+		if (range->high)
+			write_level(out, range->high, CIL_TRUE);
+		else
+			fprintf(out, "%s", range->high_str);
+		fprintf(out, ")");
+	}
+}
+
+static void write_context(FILE *out, struct cil_context *context, int print_name)
+{
+	if (print_name && context->datum.name) {
+		fprintf(out, "%s", datum_to_str(DATUM(context)));
+	} else {
+		fprintf(out, "(");
+		fprintf(out, "%s ", datum_or_str(DATUM(context->user), context->user_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(context->role), context->role_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(context->type), context->type_str));
+		if (context->range)
+			write_range(out, context->range, CIL_TRUE);
+		else
+			fprintf(out, "%s", context->range_str);
+		fprintf(out, ")");
+	}
+}
+
+static void write_ipaddr(FILE *out, struct cil_ipaddr *ipaddr)
+{
+	if (ipaddr->datum.name) {
+		fprintf(out, "%s", datum_to_str(DATUM(ipaddr)));
+	} else {
+		char buf[256];
+		if (inet_ntop(ipaddr->family, &ipaddr->ip, buf, 256) == NULL)
+			strcpy(buf, "<?IPADDR>");
+		fprintf(out, "(%s)", buf);
+	}
+}
+
+static void write_constrain(FILE *out, struct cil_constrain *cons)
+{
+	write_classperms_list(out, cons->classperms);
+	fprintf(out, " ");
+	if (cons->datum_expr)
+		write_expr(out, cons->datum_expr);
+	else
+		write_expr(out, cons->str_expr);
+}
+
+static void write_call_args(FILE *out, struct cil_list *args)
+{
+	struct cil_list_item *item;
+	int notfirst = 0;
+
+	fprintf(out, "(");
+	cil_list_for_each(item, args) {
+		struct cil_args* arg = item->data;
+		enum cil_flavor arg_flavor = arg->flavor;
+		if (notfirst)
+			fprintf(out, " ");
+		else
+			notfirst = 1;
+		switch (arg_flavor) {
+		case CIL_TYPE:
+		case CIL_ROLE:
+		case CIL_USER:
+		case CIL_SENS:
+		case CIL_CAT:
+		case CIL_BOOL:
+		case CIL_CLASS:
+		case CIL_MAP_CLASS:
+		case CIL_NAME: {
+			fprintf(out, "%s", datum_or_str(arg->arg, arg->arg_str));
+			break;
+		}
+		case CIL_CATSET: {
+			if (arg->arg) {
+				struct cil_catset *catset = (struct cil_catset *)arg->arg;
+				write_cats(out, catset->cats);
+			} else {
+				fprintf(out, "%s", arg->arg_str);
+			}
+			break;
+		}
+		case CIL_LEVEL: {
+			if (arg->arg) {
+				struct cil_level *level = (struct cil_level *)arg->arg;
+				write_level(out, level, CIL_TRUE);
+			} else {
+				fprintf(out, "%s", arg->arg_str);
+			}
+			break;
+		}
+		case CIL_LEVELRANGE: {
+			if (arg->arg) {
+				struct cil_levelrange *range = (struct cil_levelrange *)arg->arg;
+				write_range(out, range, CIL_TRUE);
+			} else {
+				fprintf(out, "%s", arg->arg_str);
+			}
+			break;
+		}
+		case CIL_IPADDR: {
+			if (arg->arg) {
+				struct cil_ipaddr *addr = (struct cil_ipaddr *)arg->arg;
+				write_ipaddr(out, addr);
+			} else {
+				fprintf(out, "%s", arg->arg_str);
+			}
+			break;
+		}
+		case CIL_CLASSPERMISSION: {
+			if (arg->arg) {
+				struct cil_classpermission *cp = (struct cil_classpermission *)arg->arg;
+				if (cp->datum.name)
+					fprintf(out, "%s", datum_to_str(DATUM(cp)));
+				else
+					write_classperms_list(out, cp->classperms);
+			} else {
+				fprintf(out, "%s", arg->arg_str);
+			}
+			break;
+		}
+		default:
+			fprintf(out, "<?ARG:%s>", datum_or_str(arg->arg, arg->arg_str));
+			break;
+		}
+	}
+	fprintf(out, ")");
+}
+
+static void write_call_args_tree(FILE *out, struct cil_tree_node *arg_node)
+{
+	while (arg_node) {
+		if (arg_node->data) {
+			fprintf(out, "%s", (char *)arg_node->data);
+		} else if (arg_node->cl_head) {
+			fprintf(out, "(");
+			write_call_args_tree(out, arg_node->cl_head);
+			fprintf(out, ")");
+		}
+		if (arg_node->next)
+			fprintf(out, " ");
+		arg_node = arg_node->next;
+	}
+}
+
+static const char *macro_param_flavor_to_string(enum cil_flavor flavor)
+{
+	const char *str;
+	switch(flavor) {
+	case CIL_TYPE:
+		str = CIL_KEY_TYPE;
+		break;
+	case CIL_ROLE:
+		str = CIL_KEY_ROLE;
+		break;
+	case CIL_USER:
+		str = CIL_KEY_USER;
+		break;
+	case CIL_SENS:
+		str = CIL_KEY_SENSITIVITY;
+		break;
+	case CIL_CAT:
+		str = CIL_KEY_CATEGORY;
+		break;
+	case CIL_CATSET:
+		str = CIL_KEY_CATSET;
+		break;
+	case CIL_LEVEL:
+		str = CIL_KEY_LEVEL;
+		break;
+	case CIL_LEVELRANGE:
+		str = CIL_KEY_LEVELRANGE;
+		break;
+	case CIL_CLASS:
+		str = CIL_KEY_CLASS;
+		break;
+	case CIL_IPADDR:
+		str = CIL_KEY_IPADDR;
+		break;
+	case CIL_MAP_CLASS:
+		str = CIL_KEY_MAP_CLASS;
+		break;
+	case CIL_CLASSPERMISSION:
+		str = CIL_KEY_CLASSPERMISSION;
+		break;
+	case CIL_BOOL:
+		str = CIL_KEY_BOOL;
+		break;
+	case CIL_STRING:
+		str = CIL_KEY_STRING;
+		break;
+	case CIL_NAME:
+		str = CIL_KEY_NAME;
+		break;
+	default:
+		str = "<?FLAVOR>";
+		break;
+	}
+	return str;
+}
+
+void cil_write_ast_node(FILE *out, struct cil_tree_node *node)
+{
+	if (!node->data) {
+		return;
+	}
+
+	switch(node->flavor) {
+	case CIL_NODE: {
+		fprintf(out, "%s\n", (char *)node->data);
+		break;
+	}
+	case CIL_BLOCK: {
+		struct cil_block *block = node->data;
+		fprintf(out, "(block %s", datum_to_str(DATUM(block)));
+		if (!node->cl_head)
+			fprintf(out, ")");
+		fprintf(out, "\n");
+		break;
+	}
+	case CIL_BLOCKINHERIT: {
+		struct cil_blockinherit *inherit = node->data;
+		fprintf(out, "(blockinherit %s)\n", datum_or_str(DATUM(inherit->block), inherit->block_str));
+		break;
+	}
+	case CIL_IN: {
+		struct cil_in *in = node->data;
+		fprintf(out, "(in %s", in->block_str);
+		if (!node->cl_head)
+			fprintf(out, ")");
+		fprintf(out, "\n");
+		break;
+	}
+	case CIL_OPTIONAL: {
+		struct cil_optional *optional = node->data;
+		fprintf(out, "(optional %s", datum_to_str(DATUM(optional)));
+		if (!node->cl_head)
+			fprintf(out, ")");
+		fprintf(out, "\n");
+		break;
+	}
+	case CIL_BOOLEANIF: {
+		struct cil_booleanif *bif = node->data;
+		fprintf(out, "(booleanif ");
+		if (bif->datum_expr)
+			write_expr(out, bif->datum_expr);
+		else
+			write_expr(out, bif->str_expr);
+		if (!node->cl_head)
+			fprintf(out, ")");
+		fprintf(out, "\n");
+		break;
+	}
+	case CIL_TUNABLEIF: {
+		struct cil_tunableif *tif = node->data;
+		fprintf(out, "(tunableif ");
+		if (tif->datum_expr)
+			write_expr(out, tif->datum_expr);
+		else
+			write_expr(out, tif->str_expr);
+		if (!node->cl_head)
+			fprintf(out, ")");
+		fprintf(out, "\n");
+		break;
+	}
+	case CIL_CONDBLOCK: {
+		struct cil_condblock *cb = node->data;
+		fprintf(out, "(%s", cb->flavor == CIL_CONDTRUE ? "true" : "false");
+		if (!node->cl_head)
+			fprintf(out, ")");
+		fprintf(out, "\n");
+		break;
+	}
+	case CIL_MACRO: {
+		struct cil_macro *macro = node->data;
+		struct cil_list_item *curr;
+		fprintf(out, "(macro %s (", datum_to_str(DATUM(macro)));
+		if (macro->params) {
+			cil_list_for_each(curr, macro->params) {
+				struct cil_param *param = curr->data;
+				fprintf(out, "(%s %s)", macro_param_flavor_to_string(param->flavor), param->str);
+			}
+		}
+		fprintf(out, ")");
+		if (!node->cl_head)
+			fprintf(out, ")");
+		fprintf(out, "\n");
+		break;
+	}
+	case CIL_CALL: {
+		struct cil_call *call = node->data;
+		fprintf(out, "(call %s", datum_or_str(DATUM(call->macro), call->macro_str));
+		if (call->args) {
+			fprintf(out, " ");
+			write_call_args(out, call->args);
+		} else if (call->args_tree) {
+			fprintf(out, " ");
+			write_call_args_tree(out, call->args_tree->root);
+		}
+		if (!node->cl_head)
+			fprintf(out, ")");
+		fprintf(out, "\n");
+		break;
+	}
+	case CIL_BLOCKABSTRACT: {
+		struct cil_blockabstract *abstract = node->data;
+		fprintf(out, "(blockabstract %s)\n", abstract->block_str);
+		break;
+	}
+	case CIL_MLS: {
+		struct cil_mls *mls = node->data;
+		fprintf(out, "(mls %s)\n", mls->value ? "true" : "false");
+		break;
+	}
+	case CIL_HANDLEUNKNOWN: {
+		struct cil_handleunknown *unknown = node->data;
+		fprintf(out, "(handleunknown ");
+		if (unknown->handle_unknown == SEPOL_ALLOW_UNKNOWN)
+			fprintf(out, "%s", CIL_KEY_HANDLEUNKNOWN_ALLOW);
+		else if (unknown->handle_unknown == SEPOL_DENY_UNKNOWN)
+			fprintf(out, "%s", CIL_KEY_HANDLEUNKNOWN_DENY);
+		else if (unknown->handle_unknown == SEPOL_REJECT_UNKNOWN)
+			fprintf(out, "%s", CIL_KEY_HANDLEUNKNOWN_REJECT);
+		else
+			fprintf(out, "<?UNKNOWN>");
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_DEFAULTUSER: {
+		struct cil_default *def = node->data;
+		fprintf(out, "(defaultuser ");
+		if (def->class_datums)
+			write_datum_list(out, def->class_datums);
+		else
+			write_string_list(out, def->class_strs);
+		if (def->object == CIL_DEFAULT_SOURCE)
+			fprintf(out, " source");
+		else if (def->object == CIL_DEFAULT_TARGET)
+			fprintf(out, " target");
+		else
+			fprintf(out, " <?DEFAULT>");
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_DEFAULTROLE: {
+		struct cil_default *def = node->data;
+		fprintf(out, "(defaultrole ");
+		if (def->class_datums)
+			write_datum_list(out, def->class_datums);
+		else
+			write_string_list(out, def->class_strs);
+		if (def->object == CIL_DEFAULT_SOURCE)
+			fprintf(out, " source");
+		else if (def->object == CIL_DEFAULT_TARGET)
+			fprintf(out, " target");
+		else
+			fprintf(out, " <?DEFAULT>");
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_DEFAULTTYPE: {
+		struct cil_default *def = node->data;
+		fprintf(out, "(defaulttype ");
+		if (def->class_datums)
+			write_datum_list(out, def->class_datums);
+		else
+			write_string_list(out, def->class_strs);
+		if (def->object == CIL_DEFAULT_SOURCE)
+			fprintf(out, " source");
+		else if (def->object == CIL_DEFAULT_TARGET)
+			fprintf(out, " target");
+		else
+			fprintf(out, " <?DEFAULT>");
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_DEFAULTRANGE: {
+		struct cil_defaultrange *def = node->data;
+		fprintf(out, "(defaultrange ");
+		if (def->class_datums)
+			write_datum_list(out, def->class_datums);
+		else
+			write_string_list(out, def->class_strs);
+		if (def->object_range == CIL_DEFAULT_SOURCE_LOW)
+			fprintf(out, " source low");
+		else if (def->object_range == CIL_DEFAULT_SOURCE_HIGH)
+			fprintf(out, " source high");
+		else if (def->object_range == CIL_DEFAULT_SOURCE_LOW_HIGH)
+			fprintf(out, " source low-high");
+		else if (def->object_range == CIL_DEFAULT_TARGET_LOW)
+			fprintf(out, " target low");
+		else if (def->object_range == CIL_DEFAULT_TARGET_HIGH)
+			fprintf(out, " target high");
+		else if (def->object_range == CIL_DEFAULT_TARGET_LOW_HIGH)
+			fprintf(out, " target low-high");
+		else
+			fprintf(out, " <?DEFAULT>");
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_CLASS: {
+		struct cil_class *class = node->data;
+		fprintf(out, "(class %s ", datum_to_str(DATUM(class)));
+		write_node_list(out, node->cl_head);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_CLASSORDER: {
+		struct cil_classorder *classorder = node->data;
+		fprintf(out, "(classorder ");
+		write_string_list(out, classorder->class_list_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_COMMON: {
+		struct cil_class *common = node->data;
+		fprintf(out, "(common %s ", datum_to_str(DATUM(common)));
+		write_node_list(out, node->cl_head);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_CLASSCOMMON: {
+		struct cil_classcommon *cc = node->data;
+		fprintf(out, "(classcommon %s %s)\n", cc->class_str, cc->common_str);
+		break;
+	}
+	case CIL_CLASSPERMISSION: {
+		struct cil_classpermission *cp = node->data;
+		fprintf(out, "(classpermission %s)\n", datum_to_str(DATUM(cp)));
+		break;
+	}
+	case CIL_CLASSPERMISSIONSET: {
+		struct cil_classpermissionset *cps = node->data;
+		fprintf(out, "(classpermissionset %s ", cps->set_str);
+		write_classperms_list(out, cps->classperms);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_MAP_CLASS: {
+		struct cil_class *map = node->data;
+		fprintf(out, "(classmap %s ", datum_to_str(DATUM(map)));
+		write_node_list(out, node->cl_head);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_CLASSMAPPING: {
+		struct cil_classmapping *mapping = node->data;
+		fprintf(out, "(classmapping %s %s ", mapping->map_class_str, mapping->map_perm_str);
+		write_classperms_list(out, mapping->classperms);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_PERMISSIONX: {
+		struct cil_permissionx *permx = node->data;
+		fprintf(out, "(permissionx %s (", datum_to_str(DATUM(permx)));
+		fprintf(out, "%s ", permx->kind == CIL_PERMX_KIND_IOCTL ? "ioctl" : "<?KIND>");
+		fprintf(out, "%s ", datum_or_str(DATUM(permx->obj), permx->obj_str));
+		write_expr(out, permx->expr_str);
+		fprintf(out, "))\n");
+		break;
+	}
+	case CIL_SID: {
+		struct cil_sid *sid = node->data;
+		fprintf(out, "(sid %s)\n", datum_to_str(DATUM(sid)));
+		break;
+	}
+	case CIL_SIDCONTEXT: {
+		struct cil_sidcontext *sidcon = node->data;
+		fprintf(out, "(sidcontext %s ", sidcon->sid_str);
+		if (sidcon->context)
+			write_context(out, sidcon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", sidcon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_SIDORDER: {
+		struct cil_sidorder *sidorder = node->data;
+		fprintf(out, "(sidorder ");
+		write_string_list(out, sidorder->sid_list_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_BOOL: {
+		struct cil_bool *boolean = node->data;
+		fprintf(out, "(boolean %s %s)\n", datum_to_str(DATUM(boolean)), boolean->value ? "true" : "false");
+		break;
+	}
+	case CIL_TUNABLE: {
+		struct cil_tunable *tunable = node->data;
+		fprintf(out, "(tunable %s %s)\n", datum_to_str(DATUM(tunable)), tunable->value ? "true" : "false");
+		break;
+	}
+	case CIL_SENS: {
+		struct cil_sens *sens = node->data;
+		fprintf(out, "(sensitivity %s)\n", datum_to_str(DATUM(sens)));
+		break;
+	}
+	case CIL_SENSALIAS: {
+		struct cil_alias *alias = node->data;
+		fprintf(out, "(sensitivityalias %s)\n", datum_to_str(DATUM(alias)));
+		break;
+	}
+	case CIL_SENSALIASACTUAL: {
+		struct cil_aliasactual *aliasactual = node->data;
+		fprintf(out, "(sensitivityaliasactual %s %s)\n", aliasactual->alias_str, aliasactual->actual_str);
+		break;
+	}
+	case CIL_CAT: {
+		struct cil_cat *cat = node->data;
+		fprintf(out, "(category %s)\n", datum_to_str(DATUM(cat)));
+		break;
+	}
+	case CIL_CATALIAS: {
+		struct cil_alias *alias = node->data;
+		fprintf(out, "(categoryalias %s)\n", datum_to_str(DATUM(alias)));
+		break;
+	}
+	case CIL_CATALIASACTUAL: {
+		struct cil_aliasactual *aliasactual = node->data;
+		fprintf(out, "(categoryaliasactual %s %s)\n", aliasactual->alias_str, aliasactual->actual_str);
+		break;
+	}
+	case CIL_CATSET: {
+		struct cil_catset *catset = node->data;
+		fprintf(out, "(categoryset %s ", datum_to_str(DATUM(catset)));
+		write_cats(out, catset->cats);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_CATORDER: {
+		struct cil_catorder *catorder = node->data;
+		fprintf(out, "(categoryorder ");
+		write_string_list(out, catorder->cat_list_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_SENSCAT: {
+		struct cil_senscat *senscat = node->data;
+		fprintf(out, "(sensitivitycategory ");
+		fprintf(out, "%s ", senscat->sens_str);
+		write_cats(out, senscat->cats);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_SENSITIVITYORDER: {
+		struct cil_sensorder *sensorder = node->data;
+		fprintf(out, "(sensitivityorder ");
+		write_string_list(out, sensorder->sens_list_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_LEVEL: {
+		struct cil_level *level = node->data;
+		fprintf(out, "(level %s ", datum_to_str(&level->datum));
+		write_level(out, level, CIL_FALSE);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_LEVELRANGE: {
+		struct cil_levelrange *lvlrange = node->data;
+		fprintf(out, "(levelrange %s ", datum_to_str(DATUM(lvlrange)));
+		write_range(out, lvlrange, CIL_FALSE);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_USER: {
+		struct cil_user *user = node->data;
+		fprintf(out, "(user %s)\n", datum_to_str(DATUM(user)));
+		break;
+	}
+	case CIL_USERATTRIBUTE: {
+		struct cil_userattribute *attr = node->data;
+		fprintf(out, "(userattribute %s)\n", datum_to_str(DATUM(attr)));
+		break;
+	}
+	case CIL_USERATTRIBUTESET: {
+		struct cil_userattributeset *attr = node->data;
+		fprintf(out, "(userattributeset %s ", attr->attr_str);
+		if (attr->datum_expr)
+			write_expr(out, attr->datum_expr);
+		else
+			write_expr(out, attr->str_expr);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_USERROLE: {
+		struct cil_userrole *userrole = node->data;
+		fprintf(out, "(userrole ");
+		fprintf(out, "%s ", datum_or_str(userrole->user, userrole->user_str));
+		fprintf(out, "%s", datum_or_str(userrole->role, userrole->role_str));
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_USERLEVEL: {
+		struct cil_userlevel *userlevel = node->data;
+		fprintf(out, "(userlevel %s ", userlevel->user_str);
+		if (userlevel->level)
+			write_level(out, userlevel->level, CIL_TRUE);
+		else
+			fprintf(out, "%s", userlevel->level_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_USERRANGE: {
+		struct cil_userrange *userrange = node->data;
+		fprintf(out, "(userrange %s ", userrange->user_str);
+		if (userrange->range)
+			write_range(out, userrange->range, CIL_TRUE);
+		else
+			fprintf(out, "%s", userrange->range_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_USERBOUNDS: {
+		struct cil_bounds *bounds = node->data;
+		fprintf(out, "(userbounds %s %s)\n", bounds->parent_str, bounds->child_str);
+		break;
+	}
+	case CIL_USERPREFIX: {
+		struct cil_userprefix *prefix = node->data;
+		fprintf(out, "(userprefix ");
+		fprintf(out, "%s ", datum_or_str(DATUM(prefix->user), prefix->user_str));
+		fprintf(out, "%s)\n", prefix->prefix_str);
+		break;
+	}
+	case CIL_SELINUXUSER: {
+		struct cil_selinuxuser *selinuxuser = node->data;
+		fprintf(out, "(selinuxuser %s ", selinuxuser->name_str);
+		fprintf(out, "%s ", datum_or_str(DATUM(selinuxuser->user), selinuxuser->user_str));
+		if (selinuxuser->range)
+			write_range(out, selinuxuser->range, CIL_TRUE);
+		else
+			fprintf(out, "%s", selinuxuser->range_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_SELINUXUSERDEFAULT: {
+		struct cil_selinuxuser *selinuxuser = node->data;
+		fprintf(out, "(selinuxuserdefault ");
+		fprintf(out, "%s ", datum_or_str(DATUM(selinuxuser->user), selinuxuser->user_str));
+		if (selinuxuser->range)
+			write_range(out, selinuxuser->range, CIL_TRUE);
+		else
+			fprintf(out, "%s", selinuxuser->range_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_ROLE: {
+		fprintf(out, "(role %s)\n", datum_to_str(node->data));
+		break;
+	}
+	case CIL_ROLEATTRIBUTE: {
+		fprintf(out, "(roleattribute %s)\n", datum_to_str(node->data));
+		break;
+	}
+	case CIL_ROLEATTRIBUTESET: {
+		struct cil_roleattributeset *attr = node->data;
+		fprintf(out, "(roleattributeset %s ", attr->attr_str);
+		if (attr->datum_expr)
+			write_expr(out, attr->datum_expr);
+		else
+			write_expr(out, attr->str_expr);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_ROLETYPE: {
+		struct cil_roletype *roletype = node->data;
+		fprintf(out, "(roletype ");
+		fprintf(out, "%s ", datum_or_str(DATUM(roletype->role), roletype->role_str));
+		fprintf(out, "%s", datum_or_str(DATUM(roletype->type), roletype->type_str));
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_ROLEBOUNDS: {
+		struct cil_bounds *bnds = node->data;
+		fprintf(out, "(rolebounds %s %s)\n", bnds->parent_str, bnds->child_str);
+		break;
+	}
+	case CIL_TYPE: {
+		fprintf(out, "(type %s)\n", datum_to_str(node->data));
+		break;
+	}
+	case CIL_TYPEALIAS: {
+		fprintf(out, "(typealias %s)\n", datum_to_str(node->data));
+		break;
+	}
+	case CIL_TYPEALIASACTUAL: {
+		struct cil_aliasactual *aliasactual = node->data;
+		fprintf(out, "(typealiasactual %s %s)\n", aliasactual->alias_str, aliasactual->actual_str);
+		break;
+	}
+	case CIL_TYPEATTRIBUTE: {
+		fprintf(out, "(typeattribute %s)\n", datum_to_str(node->data));
+		break;
+	}
+	case CIL_TYPEATTRIBUTESET: {
+		struct cil_typeattributeset *attr = node->data;
+		fprintf(out, "(typeattributeset %s ", attr->attr_str);
+		if (attr->datum_expr)
+			write_expr(out, attr->datum_expr);
+		else
+			write_expr(out, attr->str_expr);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_EXPANDTYPEATTRIBUTE: {
+		struct cil_expandtypeattribute *attr = node->data;
+		fprintf(out, "(expandtypeattribute ");
+		if (attr->attr_datums)
+			write_expr(out, attr->attr_datums);
+		else
+			write_expr(out, attr->attr_strs);
+		fprintf(out, " %s)\n", attr->expand ? "true" : "false");
+		break;
+	}
+	case CIL_TYPEPERMISSIVE: {
+		struct cil_typepermissive *tp = node->data;
+		fprintf(out, "(typepermissive ");
+		fprintf(out, "%s", datum_or_str(DATUM(tp->type), tp->type_str));
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_TYPEBOUNDS: {
+		struct cil_bounds *bounds = node->data;
+		fprintf(out, "(typebounds %s %s)\n", bounds->parent_str, bounds->child_str);
+		break;
+	}
+	case CIL_ROLEALLOW: {
+		struct cil_roleallow *roleallow = node->data;
+		fprintf(out, "(roleallow ");
+		fprintf(out, "%s ", datum_or_str(DATUM(roleallow->src), roleallow->src_str));
+		fprintf(out, "%s", datum_or_str(DATUM(roleallow->tgt), roleallow->tgt_str));
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_ROLETRANSITION: {
+		struct cil_roletransition *roletrans = node->data;
+		fprintf(out, "(roletransition ");
+		fprintf(out, "%s ", datum_or_str(DATUM(roletrans->src), roletrans->src_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(roletrans->tgt), roletrans->tgt_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(roletrans->obj), roletrans->obj_str));
+		fprintf(out, "%s", datum_or_str(DATUM(roletrans->result), roletrans->result_str));
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_AVRULE: {
+		struct cil_avrule *rule = node->data;
+		if (rule->rule_kind == AVRULE_ALLOWED)
+			fprintf(out, "(allow ");
+		else if (rule->rule_kind == AVRULE_AUDITALLOW)
+			fprintf(out, "(auditallow ");
+		else if (rule->rule_kind == AVRULE_DONTAUDIT)
+			fprintf(out, "(dontaudit ");
+		else if (rule->rule_kind == AVRULE_NEVERALLOW)
+			fprintf(out, "(neverallow ");
+		else
+			fprintf(out, "(<?AVRULE> ");
+
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->src), rule->src_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->tgt), rule->tgt_str));
+		write_classperms_list(out, rule->perms.classperms);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_AVRULEX: {
+		struct cil_avrule *rule = node->data;
+		if (rule->rule_kind == AVRULE_ALLOWED)
+			fprintf(out, "(allowx ");
+		else if (rule->rule_kind == AVRULE_AUDITALLOW)
+			fprintf(out, "(auditallowx ");
+		else if (rule->rule_kind == AVRULE_DONTAUDIT)
+			fprintf(out, "(dontauditx ");
+		else if (rule->rule_kind == AVRULE_NEVERALLOW)
+			fprintf(out, "(neverallowx ");
+		else
+			fprintf(out, "(<?AVRULEX> ");
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->src), rule->src_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->tgt), rule->tgt_str));
+		if (rule->perms.x.permx_str) {
+			fprintf(out, "%s",rule->perms.x.permx_str);
+		} else {
+			write_permx(out, rule->perms.x.permx);
+		}
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_TYPE_RULE: {
+		struct cil_type_rule *rule = node->data;
+		if (rule->rule_kind == AVRULE_TRANSITION)
+			fprintf(out, "(typetransition ");
+		else if (rule->rule_kind == AVRULE_MEMBER)
+			fprintf(out, "(typemember ");
+		else if (rule->rule_kind == AVRULE_CHANGE)
+			fprintf(out, "(typechange ");
+		else
+			fprintf(out, "(<?TYPERULE> ");
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->src), rule->src_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->tgt), rule->tgt_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->obj), rule->obj_str));
+		fprintf(out, "%s", datum_or_str(DATUM(rule->result), rule->result_str));
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_NAMETYPETRANSITION: {
+		struct cil_nametypetransition *rule = node->data;
+		fprintf(out, "(typetransition ");
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->src), rule->src_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->tgt), rule->tgt_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->obj), rule->obj_str));
+		fprintf(out, "\"%s\" ", datum_or_str(DATUM(rule->name), rule->name_str));
+		fprintf(out, "%s", datum_or_str(DATUM(rule->result), rule->result_str));
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_RANGETRANSITION: {
+		struct cil_rangetransition *rule = node->data;
+		fprintf(out, "(rangetransition ");
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->src), rule->src_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->exec), rule->exec_str));
+		fprintf(out, "%s ", datum_or_str(DATUM(rule->obj), rule->obj_str));
+		if (rule->range)
+			write_range(out, rule->range, CIL_TRUE);
+		else
+			fprintf(out, "%s", rule->range_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_CONSTRAIN: {
+		struct cil_constrain *cons = node->data;
+		fprintf(out, "(constrain ");
+		write_constrain(out, cons);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_MLSCONSTRAIN: {
+		struct cil_constrain *cons = node->data;
+		fprintf(out, "(mlsconstrain ");
+		write_constrain(out, cons);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_VALIDATETRANS: {
+		struct cil_validatetrans *vt = node->data;
+		fprintf(out, "(validatetrans ");
+		fprintf(out, "%s ", datum_or_str(DATUM(vt->class), vt->class_str));
+		if (vt->datum_expr)
+			write_expr(out, vt->datum_expr);
+		else
+			write_expr(out, vt->str_expr);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_MLSVALIDATETRANS: {
+		struct cil_validatetrans *vt = node->data;
+		fprintf(out, "(mlsvalidatetrans ");
+		fprintf(out, "%s ", datum_or_str(DATUM(vt->class), vt->class_str));
+		if (vt->datum_expr)
+			write_expr(out, vt->datum_expr);
+		else
+			write_expr(out, vt->str_expr);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_CONTEXT: {
+		struct cil_context *context = node->data;
+		fprintf(out, "(context %s ", datum_to_str(DATUM(context)));
+		write_context(out, context, CIL_FALSE);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_FILECON: {
+		struct cil_filecon *filecon = node->data;
+		fprintf(out, "(filecon ");
+		fprintf(out, "\"%s\" ", filecon->path_str);
+		if (filecon->type == CIL_FILECON_FILE)
+			fprintf(out, "%s ", CIL_KEY_FILE);
+		else if (filecon->type == CIL_FILECON_DIR)
+			fprintf(out, "%s ", CIL_KEY_DIR);
+		else if (filecon->type == CIL_FILECON_CHAR)
+			fprintf(out, "%s ", CIL_KEY_CHAR);
+		else if (filecon->type == CIL_FILECON_BLOCK)
+			fprintf(out, "%s ", CIL_KEY_BLOCK);
+		else if (filecon->type == CIL_FILECON_SOCKET)
+			fprintf(out, "%s ", CIL_KEY_SOCKET);
+		else if (filecon->type == CIL_FILECON_PIPE)
+			fprintf(out, "%s ", CIL_KEY_PIPE);
+		else if (filecon->type == CIL_FILECON_SYMLINK)
+			fprintf(out, "%s ", CIL_KEY_SYMLINK);
+		else if (filecon->type == CIL_FILECON_ANY)
+			fprintf(out, "%s ", CIL_KEY_ANY);
+		else
+			fprintf(out, "<?FILETYPE> ");
+		if (filecon->context)
+			write_context(out, filecon->context, CIL_TRUE);
+		else if (filecon->context_str)
+			fprintf(out, "%s", filecon->context_str);
+		else
+			fprintf(out, "()");
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_IBPKEYCON: {
+		struct cil_ibpkeycon *ibpkeycon = node->data;
+		fprintf(out, "(ibpkeycon %s ", ibpkeycon->subnet_prefix_str);
+		fprintf(out, "(%d %d) ", ibpkeycon->pkey_low, ibpkeycon->pkey_high);
+		if (ibpkeycon->context)
+			write_context(out, ibpkeycon->context, CIL_TRUE);
+		else if (ibpkeycon->context_str)
+			fprintf(out, "%s", ibpkeycon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_PORTCON: {
+		struct cil_portcon *portcon = node->data;
+		fprintf(out, "(portcon ");
+		if (portcon->proto == CIL_PROTOCOL_UDP)
+			fprintf(out, " udp ");
+		else if (portcon->proto == CIL_PROTOCOL_TCP)
+			fprintf(out, " tcp ");
+		else if (portcon->proto == CIL_PROTOCOL_DCCP)
+			fprintf(out, "dccp ");
+		else if (portcon->proto == CIL_PROTOCOL_SCTP)
+			fprintf(out, "sctp ");
+		else
+			fprintf(out, "<?PROTOCOL> ");
+		if (portcon->port_low == portcon->port_high)
+			fprintf(out, "%d ", portcon->port_low);
+		else
+			fprintf(out, "(%d %d) ", portcon->port_low, portcon->port_high);
+		if (portcon->context)
+			write_context(out, portcon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", portcon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_NODECON: {
+		struct cil_nodecon *nodecon = node->data;
+		fprintf(out, "(nodecon ");
+		if (nodecon->addr)
+			write_ipaddr(out, nodecon->addr);
+		else
+			fprintf(out, "%s ", nodecon->addr_str);
+		fprintf(out, " ");
+		if (nodecon->mask)
+			write_ipaddr(out, nodecon->mask);
+		else
+			fprintf(out, "%s ", nodecon->mask_str);
+		fprintf(out, " ");
+		if (nodecon->context)
+			write_context(out, nodecon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", nodecon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_GENFSCON: {
+		struct cil_genfscon *genfscon = node->data;
+		fprintf(out, "(genfscon ");
+		fprintf(out, "%s \"%s\" ", genfscon->fs_str, genfscon->path_str);
+		if (genfscon->context)
+			write_context(out, genfscon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", genfscon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_NETIFCON: {
+		struct cil_netifcon *netifcon = node->data;
+		fprintf(out, "(netifcon %s ", netifcon->interface_str);
+		if (netifcon->if_context)
+			write_context(out, netifcon->if_context, CIL_TRUE);
+		else
+			fprintf(out, "%s", netifcon->if_context_str);
+		fprintf(out, " ");
+		if (netifcon->packet_context)
+			write_context(out, netifcon->packet_context, CIL_TRUE);
+		else
+			fprintf(out, "%s", netifcon->packet_context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_IBENDPORTCON: {
+		struct cil_ibendportcon *ibendportcon = node->data;
+		fprintf(out, "(ibendportcon %s %u ", ibendportcon->dev_name_str, ibendportcon->port);
+		if (ibendportcon->context)
+			write_context(out, ibendportcon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", ibendportcon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_PIRQCON: {
+		struct cil_pirqcon *pirqcon = node->data;
+		fprintf(out, "(pirqcon %d ", pirqcon->pirq);
+		if (pirqcon->context)
+			write_context(out, pirqcon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", pirqcon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_IOMEMCON: {
+		struct cil_iomemcon *iomemcon = node->data;
+		fprintf(out, "(iomemcon (%"PRId64" %"PRId64") ", iomemcon->iomem_low, iomemcon->iomem_high);
+		if (iomemcon->context)
+			write_context(out, iomemcon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", iomemcon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_IOPORTCON: {
+		struct cil_ioportcon *ioportcon = node->data;
+		fprintf(out, "(ioportcon ");
+		if (ioportcon->ioport_low == ioportcon->ioport_high)
+			fprintf(out, "%d ", ioportcon->ioport_low);
+		else
+			fprintf(out, "(%d %d) ", ioportcon->ioport_low, ioportcon->ioport_high);
+
+		if (ioportcon->context)
+			write_context(out, ioportcon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", ioportcon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_PCIDEVICECON: {
+		struct cil_pcidevicecon *pcidevicecon = node->data;
+		fprintf(out, "(pcidevicecon %d ", pcidevicecon->dev);
+		if (pcidevicecon->context)
+			write_context(out, pcidevicecon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", pcidevicecon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_DEVICETREECON: {
+		struct cil_devicetreecon *devicetreecon = node->data;
+		fprintf(out, "(devicetreecon \"%s\" ", devicetreecon->path);
+		if (devicetreecon->context)
+			write_context(out, devicetreecon->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", devicetreecon->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_FSUSE: {
+		struct cil_fsuse *fsuse = node->data;
+		fprintf(out, "(fsuse ");
+		if (fsuse->type == CIL_FSUSE_XATTR)
+			fprintf(out, "xattr ");
+		else if (fsuse->type == CIL_FSUSE_TASK)
+			fprintf(out, "task ");
+		else if (fsuse->type == CIL_FSUSE_TRANS)
+			fprintf(out, "trans ");
+		else
+			fprintf(out, "<?TYPE> ");
+		fprintf(out, "%s ", fsuse->fs_str);
+		if (fsuse->context)
+			write_context(out, fsuse->context, CIL_TRUE);
+		else
+			fprintf(out, "%s", fsuse->context_str);
+		fprintf(out, ")\n");
+		break;
+	}
+	case CIL_POLICYCAP: {
+		struct cil_policycap *polcap = node->data;
+		fprintf(out, "(policycap %s)\n", polcap->datum.name);
+		break;
+	}
+	case CIL_IPADDR: {
+		struct cil_ipaddr *ipaddr = node->data;
+		char buf[256];
+		if (inet_ntop(ipaddr->family, &ipaddr->ip, buf, 256) == NULL)
+			strcpy(buf, "<?IPADDR>");
+		fprintf(out, "(ipaddr %s %s)\n", datum_to_str(&ipaddr->datum), buf);
+		break;
+	}
+	default :
+		fprintf(out, "(<?RULE:%s>)\n", cil_node_to_string(node));
+		break;
+	}
+}
+
+/*
+ * Tree walk data and helper functions for writing the AST of the various phases
+ */
+
+struct cil_write_ast_args {
+	FILE *out;
+	int depth;
+};
+
+/*
+ * Helper functions for writing the parse AST
+ */
+
+static int __write_parse_ast_node_helper(struct cil_tree_node *node, __attribute__((unused)) uint32_t *finished, void *extra_args)
+{
+	struct cil_write_ast_args *args = extra_args;
+
+	fprintf(args->out, "%*s", args->depth*4, "");
+	if (!node->data) {
+		if (node->cl_head)
+			fprintf(args->out, "(\n");
+		else
+			fprintf(args->out, "()\n");
+	} else {
+		char *str = (char *)node->data;
+		size_t len = strlen(str);
+		size_t i;
+
+		for (i = 0; i < len; i++) {
+			if (isspace(str[i])) {
+				fprintf(args->out, "\"%s\"\n", str);
+				return SEPOL_OK;
+			}
+		}
+
+		fprintf(args->out, "%s\n", (char *)node->data);
+	}
+
+	return SEPOL_OK;
+}
+
+static int __write_parse_ast_first_child_helper(struct cil_tree_node *node, void *extra_args)
+{
+	struct cil_write_ast_args *args = extra_args;
+	struct cil_tree_node *parent = node->parent;
+
+	if (parent->flavor != CIL_ROOT) {
+		args->depth++;
+	}
+
+	return SEPOL_OK;
+}
+
+static int __write_parse_ast_last_child_helper(struct cil_tree_node *node, void *extra_args)
+{
+	struct cil_write_ast_args *args = extra_args;
+	struct cil_tree_node *parent = node->parent;
+
+	if (parent->flavor == CIL_ROOT) {
+		return SEPOL_OK;
+	}
+
+	args->depth--;
+	fprintf(args->out, "%*s", args->depth*4, "");
+	fprintf(args->out, ")\n");
+
+	return SEPOL_OK;
+}
+
+/*
+ * Helper functions for writing the CIL AST for the build and resolve phases
+ */
+
+static int __write_cil_ast_node_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
+{
+	struct cil_write_ast_args *args = extra_args;
+
+	if (node->flavor == CIL_SRC_INFO)
+		return SEPOL_OK;
+
+	fprintf(args->out, "%*s", args->depth*4, "");
+
+	cil_write_ast_node(args->out, node);
+
+	if (node->flavor == CIL_CLASS || node->flavor == CIL_COMMON || node->flavor == CIL_MAP_CLASS) {
+		*finished = CIL_TREE_SKIP_HEAD;
+	}
+
+	return SEPOL_OK;
+}
+
+static int __write_cil_ast_first_child_helper(struct cil_tree_node *node, void *extra_args)
+{
+	struct cil_write_ast_args *args = extra_args;
+	struct cil_tree_node *parent = node->parent;
+
+	if (parent->flavor != CIL_SRC_INFO && parent->flavor != CIL_ROOT) {
+		args->depth++;
+	}
+
+	return SEPOL_OK;
+}
+
+static int __write_cil_ast_last_child_helper(struct cil_tree_node *node, void *extra_args)
+{
+	struct cil_write_ast_args *args = extra_args;
+	struct cil_tree_node *parent = node->parent;
+
+	if (parent->flavor == CIL_SRC_INFO || parent->flavor == CIL_ROOT) {
+		return SEPOL_OK;
+	}
+
+	args->depth--;
+	fprintf(args->out, "%*s", args->depth*4, "");
+	fprintf(args->out, ")\n");
+
+	return SEPOL_OK;
+}
+
+int cil_write_ast(FILE *out, enum cil_write_ast_phase phase, struct cil_tree_node *node)
+{
+	struct cil_write_ast_args extra_args;
+	int rc;
+
+	extra_args.out = out;
+	extra_args.depth = 0;
+
+	if (phase == CIL_WRITE_AST_PHASE_PARSE) {
+		rc = cil_tree_walk(node, __write_parse_ast_node_helper, __write_parse_ast_first_child_helper, __write_parse_ast_last_child_helper, &extra_args);
+	} else {
+		rc = cil_tree_walk(node, __write_cil_ast_node_helper, __write_cil_ast_first_child_helper, __write_cil_ast_last_child_helper, &extra_args);
+	}
+
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "Failed to write AST\n");
+		return SEPOL_ERR;
+	}
+
+	return SEPOL_OK;
+}
diff --git a/libsepol/cil/src/cil_write_ast.h b/libsepol/cil/src/cil_write_ast.h
new file mode 100644
index 000000000000..3f4b9d95ca1a
--- /dev/null
+++ b/libsepol/cil/src/cil_write_ast.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 Tresys Technology, LLC. 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,
+ *       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 TRESYS TECHNOLOGY, LLC ``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 TRESYS TECHNOLOGY, LLC OR CONTRIBUTORS 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.
+ *
+ * The views and conclusions contained in the software and documentation are those
+ * of the authors and should not be interpreted as representing official policies,
+ * either expressed or implied, of Tresys Technology, LLC.
+ */
+
+#ifndef CIL_WRITE_AST_H_
+#define CIL_WRITE_AST_H_
+
+#include <stdio.h>
+
+#include "cil_tree.h"
+
+enum cil_write_ast_phase {
+	CIL_WRITE_AST_PHASE_PARSE = 0,
+	CIL_WRITE_AST_PHASE_BUILD,
+	CIL_WRITE_AST_PHASE_RESOLVE,
+};
+
+void cil_write_ast_node(FILE *out, struct cil_tree_node *node);
+int cil_write_ast(FILE *out, enum cil_write_ast_phase phase, struct cil_tree_node *node);
+
+#endif /* CIL_WRITE_AST_H_ */
-- 
2.32.0