<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "language.dtd"
[
	<!-- Identifier with M4 Arg. or variables -->
	<!ENTITY charIden         "(?:\$[@#]|[\w\-\$\*\{\}&#037;])">
	<!ENTITY simpleIdentifier "(?:\$[@#]|[a-zA-Z\$&#037;])&charIden;*">
	<!ENTITY identifier       "&simpleIdentifier;(?:\.?&charIden;)*">
	<!ENTITY startIdentifier  "(?:\b[a-zA-Z]|\$[@#]|[\$&#037;])&charIden;*(?:\.?&charIden;)*">
	<!ENTITY mlsLevel         "&identifier;(?:\s*\:\s*&identifier;(?:\s*,\s*&identifier;)*)?">
]>

<!--
   SELinux File Contexts Syntax Highlighting Definition
  ===========================================================================

   This file is part of the KDE's KSyntaxHighlighting framework.

   SPDX-FileCopyrightText: 2018-2020 Nibaldo González S. <nibgonz@gmail.com>

   SPDX-License-Identifier: MIT

  ===========================================================================

  SELinux policy files with definitions of file contexts:
    - Labeling Policy Files: *.fc
    - Policy Config. Files: file_contexts, file_contexts_*, file_contexts.local,
        file_contexts.homedirs, file_contexts.template, homedir_template
    - SE for Android Policy Config. Files: service_contexts, property_contexts,
        hwservice_contexts
    - Policy Build Files: initial_sid_contexts, genfs_contexts, fs_use

  Change log:
   * Version 7 [30-Sep-2020]: Use include-keywods and other minor changes.
   * Version 5 [03-Oct-2019]: Replace unnecessary WordDetect rules.
   * Version 4 [02-Apr-2019]: Remove one indentation.
   * Version 3 [09-Sep-2018]:
      - Update itemData's style for the new Solarized color schemes.
   * Version 2 [28-Aug-2018]:
      - Some improvements. RegExp and some rules are moved to "selinux.xml".
      - Add statements keywords that use file contexts.
   * Version 1 [26-Jan-2018, by Nibaldo González]:
      - Initial version.
-->

<language name="SELinux File Contexts"
          version="9"
          kateversion="5.53"
          section="Other"
          extensions="*.fc;file_contexts;file_contexts_*;file_contexts.local;file_contexts.homedirs;file_contexts.template;homedir_template;property_contexts;service_contexts;hwservice_contexts;initial_sid_contexts;genfs_contexts;fs_use"
          priority="3"
          mimetype=""
          author="Nibaldo González (nibgonz@gmail.com)"
          license="MIT">

<highlighting>

	<list name="statements_fc">
		<include>statements_fc##SELinux Policy</include>
	</list>
	<list name="refpolicy_keywords">
		<include>refpolicy_keywords##SELinux Policy</include>
	</list>

	<contexts>

		<context name="_normal" attribute="Normal Text" lineEndContext="#stay">
			<IncludeRules context="_m4_preprocessor##SELinux Policy"/>
			<IncludeRules context="_find_comments##SELinux Policy"/>
			<RegExpr context="#stay" attribute="File Type" String="(?:\s|^)\-[bcdpls\-](?=\s|$)"/>

			<DetectChar context="_path_content" attribute="Path" char="/"/>
			<DetectChar context="_quoted" attribute="Text Quoted" char="&quot;"/>

			<!-- Content Quoted (M4).
			     NOTE: The default quotes (`text') are highlighted,
			     but another type of quotation mark can be used. -->
			<IncludeRules context="_m4_string_simple##SELinux Policy"/> <!-- `simple text' -->
			<IncludeRules context="_m4_quotes##SELinux Policy"/>

			<!-- File Contexts:  user:role:type:s0:c0 -->
			<RegExpr context="_fc_user" attribute="User" String="&startIdentifier;(?=(?:\s*\:\s*&identifier;){2}(?:\s*\:\s*&mlsLevel;(?:\s*\-\s*&mlsLevel;)?)?\b)"/>
			<StringDetect context="#stay" attribute="Other Keywords" String="&lt;&lt;none&gt;&gt;"/>

			<!-- Java-Language-Style Package Name & other Names, for SE Android (this avoid highlighting keywords) -->
			<RegExpr context="#stay" attribute="Path" String="\.?[a-zA-Z]\w*(?:(?:\.|::)\w+)+(?=[^\(]|$)" firstNonSpace="true"/>

			<!-- Functions -->
			<DetectChar context="_parentheses_content" attribute="Normal Text" char="(" beginRegion="ParenthesesBlock"/>
			<keyword context="#stay" attribute="Refpolicy Keywords" String="refpolicy_keywords"/>
			<IncludeRules context="_m4_builtin_keywords##SELinux Policy"/>
			<RegExpr context="#stay" attribute="Function" String="\b[a-zA-Z][\w\-]*(?=\()"/>

			<IncludeRules context="_m4_special_arguments##SELinux Policy"/>
			<IncludeRules context="_common_special_char##SELinux Policy"/>
			<IncludeRules context="_line_continue_escape##SELinux Policy"/>

			<!-- Default Variables -->
			<StringDetect context="_path_content" attribute="Variable" String="HOME_DIR"/>
			<StringDetect context="_path_content" attribute="Variable" String="HOME_ROOT"/>

			<DetectChar context="_path_content" attribute="Path" char="[" lookAhead="true"/>
			<Detect2Chars context="_path_content" char="%" char1="{" lookAhead="true"/>
			<RegExpr context="_path_content" attribute="Path" String="\b[\w\[\]\.@\=\*\-\$%\+!\|&amp;\^~\{\}&lt;&gt;\?\\,]+[/\\\*\?\[\{]" lookAhead="true" firstNonSpace="true"/>

			<DetectChar context="#stay" attribute="Symbol" char="{" beginRegion="Bracket"/>
			<DetectChar context="#stay" attribute="Symbol" char="}" endRegion="Bracket"/>
			<AnyChar context="#stay" attribute="Symbol" String=",;~[]"/>

			<!-- Highlight only the statements that contain definition of file contexts -->
			<keyword context="#stay" attribute="Statements" String="statements_fc"/>
			<RegExpr context="#stay" String="[a-zA-Z](?:\.?[\w\-]+)*"/>
			<HlCHex context="#pop" attribute="Number"/>
			<Int context="#pop" attribute="Number"/>
		</context>

		<context name="_parentheses_content" attribute="Normal Text" lineEndContext="#stay">
			<DetectChar context="#pop" attribute="Normal Text" char=")" endRegion="ParenthesesBlock"/>
			<!-- File Contexts:  (user:role:type,s0) -->
			<RegExpr context="_gen_context_fc_user" attribute="User" String="&startIdentifier;(?:\s*\:\s*&identifier;){2}(?:\s*,\s*&identifier;)*\b" lookAhead="true"/>
			<IncludeRules context="_normal"/>
		</context>

		<context name="_path_content" attribute="Path" lineEndContext="#pop">
			<DetectSpaces context="#pop" lookAhead="true"/>
			<IncludeRules context="_regex##SELinux Policy"/>
			<IncludeRules context="_variable"/>
		</context>
		<context name="_quoted" attribute="Text Quoted" lineEndContext="#pop">
			<DetectChar context="#pop" attribute="Text Quoted" char="&quot;"/>
			<IncludeRules context="_m4_special_arguments##SELinux Policy"/>
			<IncludeRules context="_other_variable"/>
			<IncludeRules context="_regex_quoted##SELinux Policy"/>
			<RegExpr context="#stay" attribute="Text Quoted Open" String="[^\s&quot;\[\(\\](?=\s*$)"/>
		</context>

		<!-- Variables of "file_contexts", M4 & Refpolicy -->
		<context name="_variable" attribute="Normal Text" lineEndContext="#stay">
			<WordDetect context="#stay" attribute="Variable" String="HOME_DIR"/>
			<WordDetect context="#stay" attribute="Variable" String="HOME_ROOT"/>
			<StringDetect context="#stay" attribute="Variable" String="USER"/>
			<StringDetect context="#stay" attribute="Variable" String="ROLE"/>
			<IncludeRules context="_other_variable"/>
		</context>
		<context name="_other_variable" attribute="Normal Text" lineEndContext="#stay">
			<RegExpr context="#stay" attribute="Variable" String="\%\{[a-zA-Z]\w*\}"/>
		</context>
		<context name="_fc_variable" attribute="Path" lineEndContext="#pop">
			<IncludeRules context="_m4_special_arguments##SELinux Policy"/>
			<IncludeRules context="_other_variable"/>
		</context>

		<!-- SELinux Security Contexts -->

		<!-- User -->
		<context name="_fc_user" attribute="User" lineEndContext="#pop">
			<DetectChar context="#pop!_fc_role" attribute="Normal Text" char=":"/>
		</context>
		<!-- Role -->
		<context name="_fc_role" attribute="Role" lineEndContext="#pop">
			<DetectChar context="#pop!_fc_type" attribute="Normal Text" char=":"/>
		</context>
		<!-- Type Enforcement -->
		<context name="_fc_type" attribute="Type Enforcement" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop!_after_fc">
			<RegExpr context="#pop!_fc_mls" attribute="Type Enforcement" String="\s*&identifier;(?=\s*:\s*&mlsLevel;)"/> <!-- Find MLS/MCS -->
			<RegExpr context="#pop" attribute="Type Enforcement" String="\s*&identifier;"/>
		</context>

		<!-- Start MLS/MCS -->
		<context name="_fc_mls" attribute="MLS/MCS Level" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop!_after_fc">
			<DetectChar context="#pop!_fc_find_level_range" attribute="Normal Text" char=":"/>
			<DetectSpaces context="#stay" attribute="Normal Text"/>
		</context>
		<context name="_fc_find_level_range" attribute="MLS/MCS Level"
		         lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop!_fc_level_range">
			<DetectSpaces context="#stay" attribute="Normal Text"/>
		</context>
		<!-- MLS/MCS Range (Sensitivity:Category - Sensitivity:Category) -->
		<!-- Sensitivity -->
		<context name="_fc_level_range" attribute="MLS/MCS Level" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop!_after_fc">
			<RegExpr context="#pop!_fc_level_range_cat" attribute="Normal Text" String="\s*:\s*(?=&identifier;)"/> <!-- Find Category -->
			<RegExpr context="#pop!_fc_level" attribute="Normal Text" String="\s*\-\s*(?=&mlsLevel;)"/> <!-- Find Range -->
			<IncludeRules context="_default_level"/>
		</context>
		<!-- Category -->
		<context name="_fc_level_range_cat" attribute="MLS/MCS Level" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop!_after_fc">
			<RegExpr context="#pop!_fc_level" attribute="Normal Text" String="\s*\-\s*(?=&mlsLevel;)"/> <!-- Find Range -->
			<RegExpr context="#stay" attribute="Normal Text" String="\s*,\s*(?=&simpleIdentifier;)"/>
			<IncludeRules context="_default_level"/>
		</context>
		<!-- MLS/MCS Level (Sensitivity:Category) -->
		<!-- Sensitivity -->
		<context name="_fc_level" attribute="MLS/MCS Level" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop!_after_fc">
			<RegExpr context="#pop!_fc_level_cat" attribute="Normal Text" String="\s*:\s*(?=&identifier;)"/> <!-- Find Category -->
			<IncludeRules context="_default_level"/>
		</context>
		<!-- Category -->
		<context name="_fc_level_cat" attribute="MLS/MCS Level" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop!_after_fc">
			<RegExpr context="#stay" attribute="Normal Text" String="\s*,\s*(?=&simpleIdentifier;)"/>
			<IncludeRules context="_default_level"/>
		</context>

		<context name="_default_level" attribute="Normal Text" lineEndContext="#pop">
			<RegExpr context="#stay" attribute="Normal Text" String="\s*\.\s*(?=&charIden;)"/>
			<DetectChar  context="_default_level_arg" attribute="MLS/MCS Level" char="$"/>
			<DetectChar  context="_default_level_var" attribute="MLS/MCS Level" char="%"/>
			<RegExpr context="#stay" attribute="MLS/MCS Level" String="\w+"/>
			<DetectChar context="#stay" attribute="MLS/MCS Level" char="-"/>
		</context>
		<context name="_default_level_arg" attribute="MLS/MCS Level" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
			<!-- M4 Args. It is not exact -->
			<Int context="#pop" attribute="MLS/MCS Level"/>
			<AnyChar context="#pop" attribute="MLS/MCS Level" String="#*@"/>
			<RegExpr context="#pop" attribute="MLS/MCS Level" String="\{\d+\}"/>
		</context>
		<context name="_default_level_var" attribute="MLS/MCS Level" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
			<RegExpr context="#pop" attribute="MLS/MCS Level" String="{[A-Za-z]\w*\}"/>
		</context>

		<context name="_after_fc" attribute="Normal Text" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
			<RegExpr context="#stay" attribute="Normal Text" String="\s*:\s*\w*|[:\.\w]+"/>
		</context>

		<!-- Within function "gen_context" (highlight variables and M4 args.) -->
		<!-- User -->
		<context name="_gen_context_fc_user" attribute="User" lineEndContext="#pop">
			<DetectChar context="#pop!_gen_context_role" attribute="Normal Text" char=":"/>
			<IncludeRules context="_fc_variable"/>
		</context>
		<!-- Role -->
		<context name="_gen_context_role" attribute="Role" lineEndContext="#pop">
			<DetectChar context="#pop!_before_gen_context_type" attribute="Normal Text" char=":"/>
			<IncludeRules context="_fc_variable"/>
		</context>
		<!-- Type Enforcement -->
		<context name="_before_gen_context_type" attribute="Type Enforcement"
		         lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop!_gen_context_type">
			<DetectSpaces context="#stay" attribute="Normal Text"/>
		</context>
		<context name="_gen_context_type" attribute="Type Enforcement"
		         lineEndContext="#pop!_fc_find_comma" fallthrough="true" fallthroughContext="#pop!_fc_find_comma">
			<RegExpr context="#pop!_fc_level_range" attribute="Normal Text" String="\s*:\s*(?=&identifier;)"/> <!-- Find MLS/MCS -->
			<IncludeRules context="_fc_variable"/>
			<RegExpr context="#stay" attribute="Type Enforcement" String="[\w\-]+"/>
		</context>
		<!-- Comma after file contexts -->
		<context name="_fc_find_comma" attribute="Normal Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop">
			<DetectChar context="#pop!_fc_after_comma" attribute="Normal Text" char=","/>
			<RegExpr context="#pop" String="\s*[^,\s]" lookAhead="true"/>
			<DetectSpaces />
		</context>
		<context name="_fc_after_comma" attribute="Normal Text"
		         lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop!_gen_context_level">
			<DetectSpaces context="#stay"/>
		</context>
		<!-- MLS/MCS -->
		<context name="_gen_context_level" attribute="MLS/MCS Level" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop">
			<RegExpr context="#stay" attribute="Normal Text" String="\-\s*(?=&simpleIdentifier;)"/>
			<IncludeRules context="_fc_variable"/>
			<RegExpr context="#stay" attribute="MLS/MCS Level" String="\w+"/>
			<DetectChar context="#stay" attribute="MLS/MCS Level" char="-"/>
			<AnyChar context="#stay" attribute="Normal Text" String=":.,"/>
			<DetectSpaces />
		</context>

	</contexts>

	<itemDatas>
		<itemData name="Normal Text" defStyleNum="dsNormal"   spellChecking="false"/>
		<itemData name="Text Quoted" defStyleNum="dsString"   spellChecking="false"/>
		<itemData name="Path"        defStyleNum="dsNormal"   spellChecking="false"/>
		<itemData name="Variable"    defStyleNum="dsVariable" spellChecking="false"/>
		<itemData name="File Type"   defStyleNum="dsKeyword"  spellChecking="false"/>

		<itemData name="User"             defStyleNum="dsPreprocessor"   bold="0" italic="0" underline="0" spellChecking="false"/>
		<itemData name="Role"             defStyleNum="dsInformation"    bold="0" italic="0" underline="0" spellChecking="false"/>
		<itemData name="Type Enforcement" defStyleNum="dsAttribute"      bold="0" italic="0" underline="0" spellChecking="false"/>
		<itemData name="MLS/MCS Level"    defStyleNum="dsVerbatimString" bold="0" italic="0" underline="0" spellChecking="false"/>

		<itemData name="Other Keywords"   defStyleNum="dsNormal"   italic="1" spellChecking="false"/>
		<itemData name="Statements"       defStyleNum="dsKeyword"  bold="1" spellChecking="false"/>
		<itemData name="Refpolicy Keywords" defStyleNum="dsNormal" bold="1" spellChecking="false"/>
		<itemData name="Function"         defStyleNum="dsNormal"   bold="1" spellChecking="false"/>
		<itemData name="Symbol"           defStyleNum="dsOperator" spellChecking="false"/>
		<itemData name="Number"           defStyleNum="dsComment"  bold="0" italic="0" underline="0" spellChecking="false"/>
		<itemData name="Text Quoted Open" defStyleNum="dsString"   underline="1" spellChecking="false"/>
	</itemDatas>

</highlighting>

<general>
	<keywords casesensitive="true" additionalDeliminator="&quot;&apos;&#096;" weakDeliminator="*?+.-"/>
	<comments>
		<comment name="singleLine" start="#"/>
	</comments>
</general>

</language>
<!-- kate: replace-tabs off; tab-width 3; indent-width 3; remove-trailing-spaces mod; dynamic-word-wrap off; -->
