diff --git a/README.md b/README.md index faff90f..4eecf68 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ vim-dnsserial ============= +[![Apache License 2.0](https://img.shields.io/github/license/breard-r/vim-dnsserial.svg "Apache License 2.0")](https://www.apache.org/licenses/LICENSE-2.0.html) + Another DNS-zone serial number updater. @@ -15,3 +17,105 @@ I know this not the first vim plugin available to update a DNS-zone serial numbe * it lacks functionalities. It chose not to fork the original plugin but to write a new one from scratch mainly for legal purposes, but also because I did not found the code as simple as I expected. + + +Status +------ + +This project is passively maintained. If there is no recent commits it is because: + +- I did not encountered a bug and nobody reported any. +- I am happy with the functionalities and nobody asked for new ones. +- It works on both vim and neovim. + +If you need anything, please open an issue. + + +Usage +----- + +By default, each time you save a `bindzone` file, the script will look for the DNS serial number and update it. You can also update it without saving the file by invoking the `:DNSSerialUpdate` function. + + +Patterns +-------- + +In order to be detected, the DNS serial number must match one the following pattern: + +* `YYYYMMDDXX ; serial` + - `YYYY` is the year (4 digits, must start by either `19` or `2`); + - `MM` is the month (2 digits); + - `DD` is the day (2 digits); + - `XX` is any non-negative number (1 or more digits); + - the word `serial` is not case-sensitive; + - there can be any number of blanks on each sides of the semicolon. + +* `SSSSSSSSSS ; serial` + - `SSSSSSSSSS` is the UNIX timestamp (10 digits, must start by `1`); + - the word `serial` is not case-sensitive; + - there can be any number of blanks on each sides of the semicolon. + +* `XX ; serial` + - `XX` is any non-negative number (1 or more digits); + - the word `serial` is not case-sensitive; + - there can be any number of blanks on each sides of the semicolon. + +According to those patterns, only dates between 1900 and 2999 will be detected; however this should not be a problem at all. Most importantly, only timestamps between September 9 2001 and March 17 2030 will be detected. + + +Configuration +------------- + +You can set several configuration variables in your vimrc: + +* `g:dnsserial_auto_update`: Defines whether or not the serial is updated when the zone file is saved (default is 1, set it to 0 to disable). +* `g:dnsserial_custom_patterns`: List of customs patterns that will be added to the default ones. Order matters, the first matching patters will be used. Customs patterns will be tested before the default ones. +* `g:dnsserial_patterns`: List of default patterns. It is not advised to change it. + + +Custom patterns +--------------- + +A pattern is defined by a dictionary with two keys: `regex` and `matching`. + +**regex** + +Contains the regular expression that will be used to search the document for the serial number. All the components of the serial number must be captured with parenthesis. + +**matching** + +This is a list of every components of the serial number. Each component is defined by a dictionary. The `type` key must be present and contain one of the allowed types. Depending on the type, several additional keys might be defined. Authorized types and their options are: + +* `raw`: Raw string. +* `integer`: An integer that will be incremented. +- `offset` (int): set the offset by which the integer is incremented. Default is 1. +- `padding` (int): Force the integer to be 0-padded on the associated number of digits. +- `date_reset` (bool): If set to 1 and a the serial contains a `date`, the integer will be reseted to 0 if the date is updated. Default is 0. +* `date`: A formated date that will be updated to the current one. +- `fmt` (string, mandatory) : the date format according to the `strftime()` specifications. See `:help strftime` for more details. + +**examples** + +A simple pattern matching a serial defined as an integer and followed by a comment starting by the word `serial` is: + +```js +{ + 'regex': '\(\d\+\)\s*;\s*\cserial', + 'matching': [ + {'type': 'integer'} + ] +} +``` + +The same example, but having the serial number starting by the current date (YYYYMMDD) and the integer padded on two digits: + + +```js +{ + 'regex': '\(\d\{8}\)\(\d\+\)\s*;\s*\cserial', + 'matching': [ + {'type': 'date', 'fmt': '%Y%m%d'}, + {'type': 'integer', 'padding': 2, 'date_reset': 1} + ] +} +``` diff --git a/autoload/dnsserial.vim b/autoload/dnsserial.vim index c72b448..15cca4c 100644 --- a/autoload/dnsserial.vim +++ b/autoload/dnsserial.vim @@ -12,28 +12,59 @@ " See the License for the specific language governing permissions and " limitations under the License. -function! s:IncrementSerial(old_date, old_nb) - let curr_date = strftime("%Y%m%d") - if a:old_date ==? curr_date - let curr_nb = a:old_nb + 1 - if curr_nb < 10 - let curr_nb = "0".curr_nb + +function! s:HasDateChanged(pattern, old_matchlist) + let i = 0 + for matching in a:pattern.matching + if matching.type ==? 'date' + let old_date = a:old_matchlist[i] + let new_date = strftime(matching.fmt) + if old_date !=? new_date + return 1 + endif endif - else - let curr_nb = "01" + let i += 1 + endfor + return 0 +endfunction + +function! s:GetNewValue(matching, old_value, date_changed) + if a:matching.type ==? 'date' + return strftime(a:matching.fmt) + elseif a:matching.type ==? 'integer' + let nb = a:old_value + get(a:matching, 'offset', 1) + if a:date_changed && get(a:matching, 'date_reset', 0) + let nb = 0 + endif + return printf('%0' . get(a:matching, 'padding', 1) . 'd', nb) endif - return curr_date.curr_nb + return a:old_value +endfunction + +function! s:GetNewSerial(pattern, old_matchlist) + let date_changed = s:HasDateChanged(a:pattern, a:old_matchlist) + let new_serial = [] + let i = 0 + for matching in a:pattern.matching + let new_value = s:GetNewValue(matching, a:old_matchlist[i], date_changed) + call add(new_serial, new_value) + let i += 1 + endfor + return join(new_serial, '') endfunction function! dnsserial#DNSSerialUpdate() - let pattern = '\(\d\{8}\)\(\d\+\)\s*;\s*\cserial' - if search(pattern) == 0 - echom "No serial found." - return 0 - endif - let old_pattern = matchlist(getline('.'), pattern) - let old_serial = old_pattern[1].old_pattern[2] - let new_serial = s:IncrementSerial(old_pattern[1], old_pattern[2]) - execute "s/".old_serial."/".new_serial."/" - echom "Serial updated to ".new_serial."." + let patterns = g:dnsserial_custom_patterns + g:dnsserial_patterns + for pattern in patterns + if search(pattern.regex) != 0 + let offset = len(pattern.matching) + let old_matchlist = matchlist(getline('.'), pattern.regex)[1:offset] + let old_serial = join(old_matchlist, '') + let new_serial = s:GetNewSerial(pattern, old_matchlist) + execute "s/" . old_serial . "/" . new_serial . "/" + echom "Serial updated to " . new_serial . "." + return 0 + endif + endfor + echom "No serial found." endfunction diff --git a/doc/dnsserial.txt b/doc/dnsserial.txt new file mode 100644 index 0000000..489349c --- /dev/null +++ b/doc/dnsserial.txt @@ -0,0 +1,147 @@ +*dnsserial.txt* another DNS-zone serial number updater + + ____ _ _ ____ _ _ ~ + | _ \| \ | / ___| ___ ___ _ __(_) __ _| |~ + | | | | \| \___ \/ __|/ _ \ '__| |/ _` | |~ + | |_| | |\ |___) \__ \ __/ | | | (_| | |~ + |____/|_| \_|____/|___/\___|_| |_|\__,_|_|~ + + Another DNS-zone serial number updater. + +============================================================================== +CONTENTS *DNSserialContents* + + 1. Introduction ......... |DNSserial| + 2. Usage ................ |DNSserialUsage| + 3. Patterns ............. |DNSserialPatterns| + 4. Configuration ........ |DNSserialConfiguration| + 5. Custom patterns ...... |DNSserialCustomPatterns| + 5.1 regex ........... |DNSserialCustomPatterns-regex| + 5.2 matching ........ |DNSserialCustomPatterns-matching| + 5.3 examples ........ |DNSserialCustomPatterns-examples| + 6. License .............. |DNSserialLicense| + +============================================================================== +Section 1: Introduction *DNSserial* + +I know this not the first vim plugin available to update a DNS-zone serial +number. Here is a few reasons why I chose not to use the canonical one: + + - there is no license and therefore it is not free; + - it is unmaintained; + - it is bugged; + - it lacks functionalities. + +It chose not to fork the original plugin but to write a new one from scratch +mainly for legal purposes, but also because I did not found the code as simple +as I expected. + +============================================================================== +Section 2: Usage *DNSserialUsage* + +By default, each time you save a bindzone file, the script will look for the +DNS serial number and update it. You can also update it without saving the +file by invoking the :DNSSerialUpdate function. + +============================================================================== +Section 3: Patterns *DNSserialPatterns* + +In order to be detected, the DNS serial number must match one the following +pattern: + + - YYYYMMDDXX ; serial + * YYYY is the year (4 digits, must start by either 19 or 2); + * MM is the month (2 digits); + * DD is the day (2 digits); + * XX is any non-negative number (1 or more digits); + * the word serial is not case-sensitive; + * there can be any number of blanks on each sides of the semicolon. + + - SSSSSSSSSS ; serial + * SSSSSSSSSS is the UNIX tiemstamp (10 digits, must start by 1); + * the word serial is not case-sensitive; + * there can be any number of blanks on each sides of the semicolon. + + - XX ; serial + * XX is any non-negative number (1 or more digits); + * the word serial is not case-sensitive; + * there can be any number of blanks on each sides of the semicolon. + +According to those patterns, only dates between 1900 and 2999 will be +detected; however this should not be a problem at all. Most importantly, only +timestamps between September 9 2001 and March 17 2030 will be detected. + +============================================================================== +Section 4: Configuration *DNSserialConfiguration* + +You can set several configuration variables in your vimrc: + + - g:dnsserial_auto_update: Defines whether or not the serial is updated when + the zone file is saved (default is 1, set it to 0 to disable). + - g:dnsserial_custom_patterns: List of customs patterns that will be added to + the default ones. Order matters, the first matching patters will be used. + Customs patterns will be tested before the default ones. + - g:dnsserial_patterns: List of default patterns. It is not advised to change + it. + +============================================================================== +Section 5: Custom patterns *DNSserialCustomPatterns* + +A pattern is defined by a dictionary with two keys: regex and matching. + +------------------------------------------------------------------------------ +Section 5.1: regex *DNSserialCustomPatterns-regex* + +Contains the regular expression that will be used to search the document for +the serial number. All the components of the serial number must be captured +with parenthesis. + +------------------------------------------------------------------------------ +Section 5.1: matching *DNSserialCustomPatterns-matching* + +This is a list of every components of the serial number. Each component is +defined by a dictionary. The type key must be present and contain one of the +allowed types. Depending on the type, several additional keys might be +defined. Authorized types and their options are: + + - raw: Raw string. + - integer: An integer that will be incremented. + - offset (int): set the offset by which the integer is incremented. Default + is 1. + - padding (int): Force the integer to be 0-padded on the associated number of + digits. + - date_reset (bool): If set to 1 and a the serial contains a date, the + integer will be reseted to 0 if the date is updated. Default is 0. + - date: A formated date that will be updated to the current one. + - fmt (string, mandatory) : the date format according to the strftime() + specifications. See :help strftime for more details. + +------------------------------------------------------------------------------ +Section 5.1: examples *DNSserialCustomPatterns-examples* + +A simple pattern matching a serial defined as an integer and followed by a +comment starting by the word serial is: + + { + 'regex': '\(\d\+\)\s*;\s*\cserial', + 'matching': [ + {'type': 'integer'} + ] + } + +The same example, but having the serial number starting by the current date +(YYYYMMDD) and the integer padded on two digits: + + { + 'regex': '\(\d\{8}\)\(\d\+\)\s*;\s*\cserial', + 'matching': [ + {'type': 'date', 'fmt': '%Y%m%d'}, + {'type': 'integer', 'padding': 2, 'date_reset': 1} + ] + } + +============================================================================== +Section 6: License *DNSserialLicense* + +DNSserial is Copyright 2015 Rodolphe Breard and is licensed under the Apache +License, Version 2.0. diff --git a/ftplugin/bindzone.vim b/ftplugin/bindzone.vim index 7881921..8fb7aa6 100644 --- a/ftplugin/bindzone.vim +++ b/ftplugin/bindzone.vim @@ -13,7 +13,13 @@ " limitations under the License. -augroup dnsserial - autocmd! - autocmd BufWritePre DNSSerialUpdate -augroup END +if !exists('g:dnsserial_auto_update') + let g:dnsserial_auto_update = 1 +endif + +if g:dnsserial_auto_update + augroup dnsserial + autocmd! + autocmd BufWritePre DNSSerialUpdate + augroup END +endif diff --git a/plugin/dnsserial.vim b/plugin/dnsserial.vim index 0607bde..72ee66a 100644 --- a/plugin/dnsserial.vim +++ b/plugin/dnsserial.vim @@ -12,4 +12,36 @@ " See the License for the specific language governing permissions and " limitations under the License. + +if !exists('g:dnsserial_custom_patterns') + let g:dnsserial_custom_patterns = [] +endif + +if !exists('g:dnsserial_patterns') + let g:dnsserial_patterns = [ + \{ + \'regex': '\(19\d\{2}\|20\d\{2}\)\([01]\d\)\([0-3]\d\)\(\d\+\)\s*;\s*\cserial', + \'matching': [ + \{'type': 'date', 'fmt': '%Y'}, + \{'type': 'date', 'fmt': '%m'}, + \{'type': 'date', 'fmt': '%d'}, + \{'type': 'integer', 'padding': 2, 'date_reset': 1} + \] + \}, + \{ + \'regex': '\(1\d\{9}\)\s*;\s*\cserial', + \'matching': [ + \{'type': 'date', 'fmt': '%s'}, + \] + \}, + \{ + \'regex': '\(\d\+\)\s*;\s*\cserial', + \'matching': [ + \{'type': 'integer'} + \] + \}, + \] +endif + + command! DNSSerialUpdate call dnsserial#DNSSerialUpdate()