;; ;; nagios-mode, an Emacs mode for Nagios ;; configuration files. ;; ;; Copyright Michael Orlitzky ;; ;; http://michael.orlitzky.com/ ;; ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; http://www.fsf.org/licensing/licenses/gpl.html ;; (require 'font-lock) (require 'regexp-opt) ;; Custom Variables (defcustom nagios-indent-level 2 "Number of spaces in one indentation (tab)." :type 'integer :group 'nagios ) ;; Indentation Voodoo (defun nagios-indent-line(&optional flag) "Indents a line, taking nesting into account." (nagios-indent-to (nagios-calculate-indent)) ) (defun nagios-indent-to(indent-column) "Indent the current line to column indent-column." (setq pos (point)) (beginning-of-line) (setq bol (point)) (setq pos-offset (- pos bol)) (setq first-char-offset (skip-chars-forward " \t")) (setq first-char-pos (+ bol first-char-offset)) (kill-region bol first-char-pos) (beginning-of-line) (setq pos-change (- indent-column first-char-offset)) (setq pos-offset (+ pos-offset pos-change)) (if (<= pos-offset indent-column) (setq pos-offset indent-column)) (while (< 0 indent-column) (insert " ") (setq indent-column (- indent-column 1))) (goto-char (+ bol pos-offset)) ) (defun nagios-in-block() "Determine if the point is inside of a {} block." (setq pos (point)) ;; Get the position of the last opening and closing braces, with ;; respect to the current point (setq last-opening-brace (re-search-backward "{" nil t)) (goto-char pos) (setq last-closing-brace (re-search-backward "}" nil t)) (goto-char pos) ;; If either is nil (not found) just set it to -1, so the comparison ;; doesn't die. (if (not last-opening-brace) (setq last-opening-brace -1)) (if (not last-closing-brace) (setq last-closing-brace -1)) ;; If the last brace seen in the buffer is an opening brace, we're ;; in a block. Otherwise, we aren't. (if (>= last-closing-brace last-opening-brace) nil t) ) (defun nagios-calculate-indent() "Calculate the level of indentation." ;; We're either inside a block, or we aren't. (setq indent 0) (if (nagios-in-block) (setq indent nagios-indent-level)) (setq pos (point)) (end-of-line) (setq eol (point)) (beginning-of-line) (setq bol (point)) ;; Set the indentation level to 0 if we find either brace on this ;; line. (if (re-search-forward "[{}]" eol t) (setq indent 0)) (goto-char pos) indent ) ;; Keymaps (defun nagios-insert-right-brace-and-indent() "Insert a '}' character, and indent the line." (interactive) (insert "}") (nagios-indent-line) ) (defvar nagios-mode-map() "Keymap used in nagios mode.") (when (not nagios-mode-map) (setq nagios-mode-map (make-sparse-keymap)) (define-key nagios-mode-map (read-kbd-macro "}") 'nagios-insert-right-brace-and-indent) ) ;; Regular Expression Transformations (defun regexp-word(regexp) "Takes a regular expression as an argument, and adds the word boundary condition to the beginning and end of it." ;; I consider neither a hyphen nor an underscore to be a word ;; boundary for the purpose of syntax highlighting. (concat "[^_-]\\<\\(" regexp "\\)\\>[^_-]") ) (defun regexp-alt-raw(element-list) "Takes a list of elements, and returns the string '\\(element1\\|element2...\\)'" ;; This is necessary since regexp-opt does not accept regular ;; expressions as arguments. We use regexp-opt when we can, of ;; course. (let ((regexp "\\(")) (mapcar (lambda(elem) (setq regexp (concat regexp "\\(" elem "\\)" "\\|"))) element-list) (concat (substring regexp 0 -2) ; Cut the last "\\|" "\\)") ) ) ;; Syntax Highlighting Patterns (defconst nagios-comments (eval-when-compile (regexp-alt-raw '("#+.*" ";+.*"))) ) (defconst nagios-directives (eval-when-compile (regexp-opt '("active_checks_enabled" "address" "alias" "check_command" "check_freshness" "check_interval" "check_period" "checks_enabled" "command_line" "command_name" "contact_groups" "contact_name" "contactgroup_name" "dependent_host_name" "dependent_service_description" "email" "event_handler" "event_handler_enabled" "execution_failure_criteria" "failure_prediction_enabled" "first_notification" "flap_detection_enabled" "freshness_threshold" "friday" "high_flap_threshold" "host_name" "host_notification_commands" "host_notification_options" "host_notification_period" "hostgroup_name" "hostgroups" "is_volatile" "last_notification" "low_flap_threshold" "max_check_attempts" "members" "monday" "normal_check_interval" "notification_failure_criteria" "notification_interval" "notification_options" "notification_period" "notifications_enabled" "obsess_over_service" "pager" "parallelize_check" "parents" "passive_checks_enabled" "process_perf_data" "retain_nonstatus_information" "retain_status_information" "retry_check_interval" "retry_interval" "saturday" "service_description" "service_notification_commands" "service_notification_options" "service_notification_period" "servicegroup_name" "stalking_options" "sunday" "thursday" "timeperiod_name" "tuesday" "wednesday") t)) ) (defconst nagios-macros (eval-when-compile (regexp-alt-raw '("\\$CONTACT\\(NAME\\|ALIAS\\|EMAIL\\|PAGER\\)\\$" "\\$HOST\\(NAME\\|ALIAS\\|ADDRESS\\|STATE\\)\\$" "\\$\\(ARG\\|USER\\)\\([1-9]\\|[1-2][0-9]\\|3[0-2]\\)\\$" "\\$SERVICE\\(DESC\\|STATE\\)\\$" "\\$\\(OUTPUT\\|PERFDATA\\|EXECUTIONTIME\\|LATENCY\\)\\$" "\\$NOTIFICATION\\(TYPE\\|NUMBER\\)\\$" "\\$\\(\\(SHORT\\)?DATETIME\\|DATE\\|TIME\\|TIMET\\)\\$" "\\$\\(LASTSTATECHANGE\\|STATETYPE\\)\\$" "\\$ADMIN\\(EMAIL\\|PAGER\\)\\$" "\\$\\(SERVICE\\|HOST\\)ATTEMPT\\$"))) ) (defconst nagios-definitions (eval-when-compile (regexp-alt-raw '("define +\\(host\\|service\\|timeperiod\\|contact\\|command\\)" "define +\\(host\\|contact\\|service\\)group" "define +\\(service\\|host\\)dependency" "define +\\(service\\|host\\|hostgroup\\)escalation"))) ) (defconst nagios-special (eval-when-compile (regexp-opt '("name" "register" "use") t)) ) ;; The One True Font Locking Variable (defvar nagios-font-lock-keywords (list ;; This first bit of ugliness allows us to override any ;; other font-locking with the comment font. (cons nagios-comments '(0 font-lock-comment-delimiter-face t)) ;; The rest just map regular expressions to font faces. (cons (regexp-word nagios-directives) font-lock-variable-name-face) (cons (regexp-word nagios-macros) font-lock-constant-face) (cons (regexp-word nagios-definitions) font-lock-function-name-face) (cons (regexp-word nagios-special) font-lock-keyword-face)) "Rules for highlighting Nagios configuration files." ) ;; Main Mode Function (defun nagios-mode() "Major mode for editing Nagios configuration files." (interactive) ;; Initializing. This is actually important to cover ;; up some Emacs stupidity. Font locking won't occur ;; without it. (kill-all-local-variables) ;; Set up indentation handling using the functions ;; defined earlier. (make-local-variable 'indent-line-function) (setq indent-line-function 'nagios-indent-line) ;; Configure font locking. Set the defaults to something ;; sensible, defined earlier. (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '(nagios-font-lock-keywords nil t)) ;; Keyboard Mapping (use-local-map nagios-mode-map) ;; Rock and roll. (setq mode-name "nagios" major-mode 'nagios-mode) ;; I don't /think/ I need to define this before attempting ;; to run it. Users can define it if they want. (run-hooks 'nagios-mode-hook) ) (provide 'nagios-mode)