diff --git a/man/sesearch.1 b/man/sesearch.1
index 967d6e2d..452c7902 100644
--- a/man/sesearch.1
+++ b/man/sesearch.1
@@ -105,6 +105,8 @@ Print version information and exit.
 Print additional informational messages.
 .IP "--debug"
 Enable debugging output.
+.IP "-C, --cil"
+Set output format to CIL.
 
 .SH EXAMPLE
 .nf
diff --git a/sesearch b/sesearch
index 0b6fdb46..22ab0587 100755
--- a/sesearch
+++ b/sesearch
@@ -21,6 +21,7 @@ parser.add_argument("policy", help="Path to the SELinux policy to search.", narg
 parser.add_argument("-v", "--verbose", action="store_true",
                     help="Print extra informational messages")
 parser.add_argument("--debug", action="store_true", dest="debug", help="Enable debugging.")
+parser.add_argument("-C", "--cil", action="store_true", dest="gen_cil", help="Enable CIL output.")
 
 rtypes = parser.add_argument_group("TE Rule Types")
 rtypes.add_argument("-A", action="store_true", help="Search allow and allowxperm rules.")
@@ -143,7 +144,7 @@ else:
         warnings.simplefilter("ignore")
 
 try:
-    p = setools.SELinuxPolicy(args.policy)
+    p = setools.SELinuxPolicy(args.policy, gen_cil=args.gen_cil)
 
     if args.tertypes:
         terq = setools.TERuleQuery(p,
diff --git a/setools/policyrep.pyi b/setools/policyrep.pyi
index 445a2b4d..58335d5e 100644
--- a/setools/policyrep.pyi
+++ b/setools/policyrep.pyi
@@ -135,6 +135,7 @@ class ConditionalOperator(PolicyObject):
     evaluate: Callable = ...
     precedence: int = ...
     unary: bool = ...
+    cil_text: str = ...
 
 class Constraint(BaseConstraint):
     perms: frozenset[str] = ...
@@ -376,6 +377,7 @@ class SELinuxPolicy:
     dontaudit_count: int = ...
     dontauditxperm_count: int = ...
     fs_use_count: int = ...
+    gen_cil: bool = ...
     genfscon_count: int = ...
     handle_unknown: "HandleUnknown" = ...
     ibendportcon_count: int = ...
diff --git a/setools/policyrep/boolcond.pxi b/setools/policyrep/boolcond.pxi
index 93e48e1d..f9a2473d 100644
--- a/setools/policyrep/boolcond.pxi
+++ b/setools/policyrep/boolcond.pxi
@@ -78,50 +78,77 @@ cdef class Conditional(PolicyObject):
         return other in self.booleans
 
     def __str__(self):
-        # sepol representation is in postfix notation.  This code
-        # converts it to infix notation.  Parentheses are added
-        # to ensure correct expressions, though they may end up
-        # being overused.  Set previous operator at start to the
-        # highest precedence (NOT) so if there is a single binary
-        # operator, no parentheses are output
-        stack = []
-        prev_op_precedence = 5
-
-        for expr_node in self.expression():
-            if isinstance(expr_node, Boolean):
-                # append the boolean name
-                stack.append(str(expr_node))
-            elif expr_node.unary:
-                operand = stack.pop()
-                operator = str(expr_node)
-                op_precedence = expr_node.precedence
-
-                # NOT is the highest precedence, so only need
-                # parentheses if the operand is a subexpression
-                if isinstance(operand, list):
-                    subexpr = [operator, "(", operand, ")"]
+        if self.policy.gen_cil:
+            # sepol representation is in postfix notation.  This code
+            # converts it to prefix notation used by CIL.  Parentheses
+            # are always needed.
+            stack = []
+
+            for expr_node in self.expression():
+                if isinstance(expr_node, Boolean):
+                    # append the boolean name
+                    stack.append(str(expr_node))
+                elif expr_node.unary:
+                    operand = stack.pop()
+                    operator = str(expr_node)
+
+                    subexpr = ["(", operator, " ", operand, ")"]
+
+                    stack.append(subexpr)
                 else:
-                    subexpr = [operator, operand]
+                    operand1 = stack.pop()
+                    operand2 = stack.pop()
+                    operator = str(expr_node)
 
-                stack.append(subexpr)
-                prev_op_precedence = op_precedence
-            else:
-                operand1 = stack.pop()
-                operand2 = stack.pop()
-                operator = str(expr_node)
-                op_precedence = expr_node.precedence
+                    subexpr = ["(", operator, " ", operand1, " ", operand2, ")"]
+
+                    stack.append(subexpr)
+            return ''.join(flatten_list(stack))
 
-                if prev_op_precedence > op_precedence:
-                    # if previous operator is of higher precedence
-                    # no parentheses are needed.
-                    subexpr = [operand1, operator, operand2]
+        else:
+            # This code converts sepol to infix notation.  Parentheses are
+            # added to ensure correct expressions, though they may end up
+            # being overused.  Set previous operator at start to the
+            # highest precedence (NOT) so if there is a single binary
+            # operator, no parentheses are output
+            stack = []
+            prev_op_precedence = 5
+
+            for expr_node in self.expression():
+                if isinstance(expr_node, Boolean):
+                    # append the boolean name
+                    stack.append(str(expr_node))
+                elif expr_node.unary:
+                    operand = stack.pop()
+                    operator = str(expr_node)
+                    op_precedence = expr_node.precedence
+
+                    # NOT is the highest precedence, so only need
+                    # parentheses if the operand is a subexpression
+                    if isinstance(operand, list):
+                        subexpr = [operator, "(", operand, ")"]
+                    else:
+                        subexpr = [operator, operand]
+
+                        stack.append(subexpr)
+                        prev_op_precedence = op_precedence
                 else:
-                    subexpr = ["(", operand1, operator, operand2, ")"]
+                    operand1 = stack.pop()
+                    operand2 = stack.pop()
+                    operator = str(expr_node)
+                    op_precedence = expr_node.precedence
+
+                    if prev_op_precedence > op_precedence:
+                        # if previous operator is of higher precedence
+                        # no parentheses are needed.
+                        subexpr = [operand1, operator, operand2]
+                    else:
+                        subexpr = ["(", operand1, operator, operand2, ")"]
 
-                stack.append(subexpr)
-                prev_op_precedence = op_precedence
+                        stack.append(subexpr)
+                        prev_op_precedence = op_precedence
 
-        return ' '.join(flatten_list(stack))
+            return ' '.join(flatten_list(stack))
 
     def __hash__(self):
         return hash(self.key)
@@ -222,6 +249,7 @@ cdef class ConditionalOperator(PolicyObject):
 
     cdef:
         str text
+        str cil_text
         readonly int precedence
         # T/F the operator is unary
         readonly bint unary
@@ -231,12 +259,12 @@ cdef class ConditionalOperator(PolicyObject):
         readonly object evaluate
 
     _cond_expr_val_to_details = {
-        sepol.COND_NOT: ("!", 5, lambda x: not x),
-        sepol.COND_OR: ("||", 1, lambda x, y: x or y),
-        sepol.COND_AND: ("&&", 3, lambda x, y: x and y),
-        sepol.COND_XOR: ("^", 2, lambda x, y: x ^ y),
-        sepol.COND_EQ: ("==", 4, lambda x, y: x == y),
-        sepol.COND_NEQ: ("!=", 4, lambda x, y: x != y)}
+        sepol.COND_NOT: ("!", "not", 5, lambda x: not x),
+        sepol.COND_OR: ("||", "or", 1, lambda x, y: x or y),
+        sepol.COND_AND: ("&&", "and", 3, lambda x, y: x and y),
+        sepol.COND_XOR: ("^", "xor", 2, lambda x, y: x ^ y),
+        sepol.COND_EQ: ("==", "eq", 4, lambda x, y: x == y),
+        sepol.COND_NEQ: ("!=", "ne", 4, lambda x, y: x != y)}
 
     @staticmethod
     cdef inline ConditionalOperator factory(SELinuxPolicy policy, sepol.cond_expr_t *symbol):
@@ -245,11 +273,14 @@ cdef class ConditionalOperator(PolicyObject):
         op.policy = policy
         op.key = <uintptr_t>symbol
         op.unary = symbol.expr_type == sepol.COND_NOT
-        op.text, op.precedence, op.evaluate = op._cond_expr_val_to_details[symbol.expr_type]
+        op.text, op.cil_text, op.precedence, op.evaluate = op._cond_expr_val_to_details[symbol.expr_type]
         return op
 
     def __str__(self):
-        return self.text
+        if self.policy.gen_cil:
+            return self.cil_text
+        else:
+            return self.text
 
     def statement(self):
         raise NoStatement
diff --git a/setools/policyrep/mls.pxi b/setools/policyrep/mls.pxi
index 908fd7ae..9966e88a 100644
--- a/setools/policyrep/mls.pxi
+++ b/setools/policyrep/mls.pxi
@@ -168,17 +168,20 @@ cdef class BaseMLSLevel(PolicyObject):
         cats = sorted(self._categories, key=lambda k: k._value)
 
         if cats:
-            # generate short category notation
-            shortlist = []
-            for _, i in itertools.groupby(cats, key=lambda k,
-                                          c=itertools.count(): k._value - next(c)):
-                group = list(i)
-                if len(group) > 1:
-                    shortlist.append(f"{group[0]}.{group[-1]}")
-                else:
-                    shortlist.append(str(group[0]))
-
-            lvl += ":" + ','.join(shortlist)
+            if self.policy.gen_cil:
+                lvl += " (" + " ".join(c.name for c in cats) + ")"
+            else:
+                # generate short category notation
+                shortlist = []
+                for _, i in itertools.groupby(cats, key=lambda k,
+                                              c=itertools.count(): k._value - next(c)):
+                    group = list(i)
+                    if len(group) > 1:
+                        shortlist.append(f"{group[0]}.{group[-1]}")
+                    else:
+                        shortlist.append(str(group[0]))
+
+                lvl += ":" + ','.join(shortlist)
 
         return lvl
 
@@ -431,10 +434,16 @@ cdef class Range(PolicyObject):
         return r
 
     def __str__(self):
-        if self.high == self.low:
-            return str(self.low)
+        if self.policy.gen_cil:
+            if self.high == self.low:
+                return f"({self.low})"
+
+            return f"({self.low} {self.high})"
+        else:
+            if self.high == self.low:
+                return str(self.low)
 
-        return f"{self.low} - {self.high}"
+            return f"{self.low} - {self.high}"
 
     def __hash__(self):
         return hash(str(self))
diff --git a/setools/policyrep/mlsrule.pxi b/setools/policyrep/mlsrule.pxi
index 402bb2d8..1d6243d3 100644
--- a/setools/policyrep/mlsrule.pxi
+++ b/setools/policyrep/mlsrule.pxi
@@ -67,7 +67,10 @@ cdef class MLSRule(PolicyRule):
             yield self
 
     def statement(self):
-        return f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.default};"
+        if self.policy.gen_cil:
+            return f"(rangetransition {self.source} {self.target} {self.tclass} {self.default})"
+        else:
+            return f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.default};"
 
 
 #
diff --git a/setools/policyrep/rbacrule.pxi b/setools/policyrep/rbacrule.pxi
index 818ed483..3e93b838 100644
--- a/setools/policyrep/rbacrule.pxi
+++ b/setools/policyrep/rbacrule.pxi
@@ -63,7 +63,10 @@ cdef class RoleAllow(PolicyRule):
             yield self
 
     def statement(self):
-        return f"{self.ruletype} {self.source} {self.target};"
+        if self.policy.gen_cil:
+            return f"(roleallow {self.source} {self.target})"
+        else:
+            return f"{self.ruletype} {self.source} {self.target};"
 
 
 cdef class RoleTransition(PolicyRule):
@@ -122,7 +125,10 @@ cdef class RoleTransition(PolicyRule):
             yield self
 
     def statement(self):
-        return f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.default};"
+        if self.policy.gen_cil:
+            return f"(roletransition {self.source} {self.target} {self.tclass} {self.default})"
+        else:
+            return f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.default};"
 
 
 #
diff --git a/setools/policyrep/role.pxi b/setools/policyrep/role.pxi
index a83d9de6..e7f66b50 100644
--- a/setools/policyrep/role.pxi
+++ b/setools/policyrep/role.pxi
@@ -34,15 +34,21 @@ cdef class Role(PolicySymbol):
 
     def statement(self):
         cdef size_t count
-        types = list(str(t) for t in self._types)
-        count = len(types)
-        stmt = f"role {self.name}"
-        if count == 0:
-            return f"role {self.name};"
-        if count == 1:
-            return f"role {self.name} types {types[0]};"
-
-        return f"role {self.name} types {{ {' '.join(sorted(types))} }};"
+        if self.policy.gen_cil:
+            stmt = ""
+            for t in self._types:
+                stmt += f"(roletype {self} {t})\n"
+            return stmt
+        else:
+            types = list(str(t) for t in self._types)
+            count = len(types)
+            stmt = f"role {self.name}"
+            if count == 0:
+                return f"role {self.name};"
+            if count == 1:
+                return f"role {self.name} types {types[0]};"
+
+            return f"role {self.name} types {{ {' '.join(sorted(types))} }};"
 
 
 #
diff --git a/setools/policyrep/selinuxpolicy.pxi b/setools/policyrep/selinuxpolicy.pxi
index d7b59fdf..2f3c6a8a 100644
--- a/setools/policyrep/selinuxpolicy.pxi
+++ b/setools/policyrep/selinuxpolicy.pxi
@@ -36,6 +36,7 @@ cdef class SELinuxPolicy:
         dict category_alias_map
         dict sensitivity_alias_map
         object __weakref__
+        bint gen_cil
 
         # Public attributes:
         readonly str path
@@ -44,7 +45,7 @@ cdef class SELinuxPolicy:
         readonly unsigned int version
         readonly bint mls
 
-    def __cinit__(self, policyfile=None):
+    def __cinit__(self, policyfile=None, gen_cil=False):
         """
         Parameter:
         policyfile  Path to a policy to open.
@@ -60,6 +61,8 @@ cdef class SELinuxPolicy:
         else:
             self._load_running_policy()
 
+        self.gen_cil = gen_cil
+
     def __dealloc__(self):
         PyMem_Free(self.cat_val_to_struct)
         PyMem_Free(self.level_val_to_struct)
diff --git a/setools/policyrep/terule.pxi b/setools/policyrep/terule.pxi
index e6bbcb0f..20fc54f7 100644
--- a/setools/policyrep/terule.pxi
+++ b/setools/policyrep/terule.pxi
@@ -195,20 +195,27 @@ cdef class AVRule(BaseTERule):
 
     def statement(self):
         if not self.rule_string:
-            self.rule_string = f"{self.ruletype} {self.source} {self.target}:{self.tclass} "
-
-            # allow/dontaudit/auditallow/neverallow rules
-            perms = self.perms
-            if len(perms) > 1:
-                self.rule_string += f"{{ {' '.join(sorted(perms))} }};"
+            if self.policy.gen_cil:
+                self.rule_string = f"({self.ruletype} {self.source} {self.target} ({self.tclass} ({' '.join(sorted(self.perms))})))"
+                try:
+                    self.rule_string = f"(booleanif ({self.conditional})\n\t({str(self.conditional_block).lower())}\n\t\t{self.rule_string}\n\t)\n)"
+                except RuleNotConditional:
+                    pass
             else:
-                # convert to list since sets cannot be indexed
-                self.rule_string += f"{list(perms)[0]};"
+                self.rule_string = f"{self.ruletype} {self.source} {self.target}:{self.tclass} "
 
-            try:
-                self.rule_string += f" [ {self.conditional} ]:{self.conditional_block}"
-            except RuleNotConditional:
-                pass
+                # allow/dontaudit/auditallow/neverallow rules
+                perms = self.perms
+                if len(perms) > 1:
+                    self.rule_string += f"{{ {' '.join(sorted(perms))} }};"
+                else:
+                    # convert to list since sets cannot be indexed
+                    self.rule_string += f"{list(perms)[0]};"
+
+                try:
+                    self.rule_string += f" [ {self.conditional} ]:{self.conditional_block}"
+                except RuleNotConditional:
+                    pass
 
         return self.rule_string
 
@@ -379,14 +386,17 @@ cdef class AVRuleXperm(BaseTERule):
 
     def statement(self):
         if not self.rule_string:
-            self.rule_string = f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.xperm_type} "
-
-            # generate short permission notation
-            perms = self.perms
-            if perms.ranges() > 1:
-                self.rule_string += f"{{ {perms} }};"
+            if self.policy.gen_cil:
+                self.rule_string = f"({self.ruletype} {self.source} {self.target} ({self.tclass} ({self.xperm_type} {self.perms})))"
             else:
-                self.rule_string += f"{perms};"
+                self.rule_string = f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.xperm_type} "
+
+                # generate short permission notation
+                perms = self.perms
+                if perms.ranges() > 1:
+                    self.rule_string += f"{{ {perms} }};"
+                else:
+                    self.rule_string += f"{perms};"
 
         return self.rule_string
 
@@ -454,12 +464,20 @@ cdef class TERule(BaseTERule):
 
     def statement(self):
         if not self.rule_string:
-            self.rule_string = f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.default};"
+            if self.policy.gen_cil:
+                self.rule_string = f"({self.ruletype} {self.source} {self.target} ({self.tclass} ({self.default})))"
+                try:
+                    self.rule_string = f"(booleanif ({self.conditional})\n\t({str(self.conditional_block).lower())}\n\t\t{self.rule_string}\n\t)\n)"
+                except RuleNotConditional:
+                    pass
 
-            try:
-                self.rule_string += f" [ {self.conditional} ]:{self.conditional_block}"
-            except RuleNotConditional:
-                pass
+            else:
+                self.rule_string = f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.default};"
+
+                try:
+                    self.rule_string += f" [ {self.conditional} ]:{self.conditional_block}"
+                except RuleNotConditional:
+                    pass
 
         return self.rule_string
 
@@ -531,7 +549,10 @@ cdef class FileNameTERule(BaseTERule):
             yield self
 
     def statement(self):
-        return f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.default} {self.filename};"
+        if self.policy.gen_cil:
+            return f"({self.ruletype} {self.source} {self.target} ({self.tclass} {self.default} {self.filename}))"
+        else:
+            return f"{self.ruletype} {self.source} {self.target}:{self.tclass} {self.default} {self.filename};"
 
 
 #
