Commit 8f6550b3 authored by Peder O. Klingenberg's avatar Peder O. Klingenberg Committed by Glenn Morris

New dns-mode command for IPv6 address conversion

This converts IPv6 addresses to a format suitable for
reverse lookup zone files.  (Bug#26820)
* lisp/textmodes/dns-mode.el (dns-mode-map, dns-mode-menu):
Add dns-mode-ipv6-to-nibbles.
(dns-mode-ipv6-to-nibbles, dns-mode-reverse-and-expand-ipv6):
New functions.
* test/lisp/dns-mode-tests.el: New file.
; * etc/NEWS: Mention this.
parent ef9f5c67
......@@ -850,6 +850,10 @@ This is done with the help of 'c-or-c++-mode' function which analyses
contents of the buffer to determine whether it's a C or C++ source
** New DNS mode command 'dns-mode-ipv6-to-nibbles' to convert IPv6 addresses
to a format suitable for reverse lookup zone files.
** Flymake
......@@ -147,6 +147,7 @@ manually with \\[dns-mode-soa-increment-serial]."
(defvar dns-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-s" 'dns-mode-soa-increment-serial)
(define-key map "\C-c\C-e" 'dns-mode-ipv6-to-nibbles)
"Keymap for DNS master file mode.")
......@@ -158,7 +159,8 @@ manually with \\[dns-mode-soa-increment-serial]."
(easy-menu-define dns-mode-menu dns-mode-map
"DNS Menu."
["Increment SOA serial" dns-mode-soa-increment-serial t]))
["Increment SOA serial" dns-mode-soa-increment-serial t]
["Convert IPv6 address to nibbles" dns-mode-ipv6-to-nibbles t]))
;; Mode.
......@@ -254,6 +256,101 @@ This function is run from `before-save-hook'."
;; We return nil in case this is used in write-contents-functions.
(defun dns-mode-ipv6-to-nibbles (&optional negate-prefix)
"Convert an IPv6 address around or before point.
Replace the address by its for use in
reverse zone files, placing the original address in the kill ring.
The address can be: a complete address (no prefix designator);
with a normal prefix designator (e.g. /48), in which case only
the required number of nibbles are output; or with a negative
prefix designator (e.g. /-112), in which case only the part of
the address *not* covered by the absolute value of the prefix
length is output, as a relative address (without \"\" at
the end). This is useful when $ORIGIN is specified in the zone file.
Optional prefix argument NEGATE-PREFIX negates the value of the
detected prefix length.
2001:db8::12 =>
2001:db8::12/32 =>
2001:db8::12/-32 =>
::42/112 (with prefix argument) =>"
(interactive "P")
(skip-syntax-backward " ")
(skip-syntax-backward "w_.")
(re-search-forward "\\([[:xdigit:]:]+\\)\\(/-?[0-9]\\{2,3\\}\\)?")
(kill-new (match-string 0))
(let ((address (match-string 1))
(prefix-length (match-string 2)))
(when prefix-length
(setq prefix-length (string-to-number (substring prefix-length 1)))
(if negate-prefix
(setq prefix-length (- prefix-length))))
(dns-mode-reverse-and-expand-ipv6 address prefix-length)))))
(defun dns-mode-reverse-and-expand-ipv6 (address &optional prefix-length)
"Convert an IPv6 address to (parts of) an nibble format.
ADDRESS is an IPv6 address in the usual colon-separated
format, without a prefix designator at the end.
Optional PREFIX-LENGTH is a number whose absolute value is the
length in bits of the network part of the address. If nil,
return an absolute address representing the full IPv6 address.
If positive, return an absolute address representing the network
prefix indicated. If negative, return a relative address
representing the host parts of the address with respect to the
indicated network prefix.
See `dns-mode-ipv6-to-nibbles' for examples."
(let* ((chunks (split-string address ":"))
(prefix-length-nibbles (if prefix-length
(ceiling (abs prefix-length) 4)
(filler-chunks (- 8 (length (remove "" chunks))))
(apply #'concat
(cl-loop with filler-done = nil
for chunk in chunks
if (and (not filler-done)
(string= "" chunk))
append (prog1
(cl-loop repeat filler-chunks
collect "0000")
(setq filler-done t))
if (not (string= "" chunk))
collect (format "%04x"
(string-to-number chunk 16)))))
(nreverse (if (and prefix-length
(cl-minusp prefix-length))
(substring expanded-address prefix-length-nibbles)
(substring expanded-address 0 prefix-length-nibbles)))))
(cl-loop for char across rev-address-nibbles
(insert char)
(insert "."))
(if (and prefix-length
(cl-minusp prefix-length))
(delete-char -1)
(insert ""))
(insert " ")
(provide 'dns-mode)
;;; dns-mode.el ends here
;;; dns-mode-tests.el --- Test suite for dns-mode -*- lexical-binding: t; -*-
;; Copyright (C) 2017 Free Software Foundation, Inc.
;; Author: Peder O. Klingenberg <>
;; Keywords: dns zone
;; This file is part of GNU Emacs.
;; GNU Emacs 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.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <>.
;;; Code:
(require 'ert)
(require 'dns-mode)
;;; IPv6 reverse zones
(ert-deftest dns-mode-ipv6-conversion ()
(let ((address "2001:db8::42"))
(should (equal (dns-mode-reverse-and-expand-ipv6 address)
" "))
(should (equal (dns-mode-reverse-and-expand-ipv6 address 32)
" "))
(should (equal (dns-mode-reverse-and-expand-ipv6 address -112)
" "))))
(ert-deftest dns-mode-ipv6-text-replacement ()
(let ((address "2001:db8::42/32"))
;; Conversion with point directly after address
(insert address)
(dns-mode-ipv6-to-nibbles nil)
(should (equal (buffer-string) " "))
;; Kill ring contains the expected
(should (equal (buffer-string) address))
;; Point at beginning of address (and prefix arg to command)
(goto-char (point-min))
(dns-mode-ipv6-to-nibbles t)
(should (equal (buffer-string) " "))
;; Point separated from address by whitespace
(insert address)
(insert " ")
(dns-mode-ipv6-to-nibbles nil)
(should (equal (buffer-string) " ")))))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment