diff --git a/README.md b/README.md
index 837e98ea..3f80902e 100644
--- a/README.md
+++ b/README.md
@@ -238,6 +238,7 @@ option.
| ` --rpm-qi` | `rpm -qi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rpm_qi) |
| ` --rsync` | `rsync` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync) |
| ` --rsync-s` | `rsync` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync_s) |
+| ` --semver` | Semantic Version string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/semver) |
| ` --sfdisk` | `sfdisk` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sfdisk) |
| ` --shadow` | `/etc/shadow` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/shadow) |
| ` --ss` | `ss` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ss) |
diff --git a/completions/jc_bash_completion.sh b/completions/jc_bash_completion.sh
index da908778..4c0ada3a 100644
--- a/completions/jc_bash_completion.sh
+++ b/completions/jc_bash_completion.sh
@@ -4,7 +4,7 @@ _jc()
jc_about_options jc_about_mod_options jc_help_options jc_special_options
jc_commands=(acpi airport arp blkid chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo)
- jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cef --cef-s --chage --cksum --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --iostat --iostat-s --ip-address --iptables --iw-scan --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --passwd --pci-ids --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo)
+ jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cef --cef-s --chage --cksum --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --iostat --iostat-s --ip-address --iptables --iw-scan --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --passwd --pci-ids --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo)
jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --unbuffer -u --yaml-out -y)
jc_about_options=(--about -a)
jc_about_mod_options=(--pretty -p --yaml-out -y --monochrome -m --force-color -C)
diff --git a/completions/jc_zsh_completion.sh b/completions/jc_zsh_completion.sh
index 87432670..74f340d0 100644
--- a/completions/jc_zsh_completion.sh
+++ b/completions/jc_zsh_completion.sh
@@ -99,7 +99,7 @@ _jc() {
'xrandr:run "xrandr" command with magic syntax.'
'zipinfo:run "zipinfo" command with magic syntax.'
)
- jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cef --cef-s --chage --cksum --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --iostat --iostat-s --ip-address --iptables --iw-scan --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --passwd --pci-ids --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo)
+ jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cef --cef-s --chage --cksum --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --iostat --iostat-s --ip-address --iptables --iw-scan --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --passwd --pci-ids --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo)
jc_parsers_describe=(
'--acpi:`acpi` command parser'
'--airport:`airport -I` command parser'
@@ -234,6 +234,7 @@ _jc() {
'--rpm-qi:`rpm -qi` command parser'
'--rsync:`rsync` command parser'
'--rsync-s:`rsync` command streaming parser'
+ '--semver:Semantic Version string parser'
'--sfdisk:`sfdisk` command parser'
'--shadow:`/etc/shadow` file parser'
'--ss:`ss` command parser'
diff --git a/docs/parsers/semver.md b/docs/parsers/semver.md
new file mode 100644
index 00000000..64ba7373
--- /dev/null
+++ b/docs/parsers/semver.md
@@ -0,0 +1,75 @@
+[Home](https://kellyjonbrazil.github.io/jc/)
+
+
+# jc.parsers.semver
+
+jc - JSON Convert Semantic Version string parser
+
+This parser conforms to the specification at https://semver.org/
+
+Usage (cli):
+
+ $ echo 1.2.3-rc.1+44837 | jc --semver
+
+Usage (module):
+
+ import jc
+ result = jc.parse('semver', semver_string)
+
+Schema:
+
+Strings that do not strictly conform to the specification will return an
+empty object.
+
+ {
+ "major": integer,
+ "minor": integer,
+ "patch": integer,
+ "prerelease": string/null,
+ "build": string/null
+ }
+
+Examples:
+
+ $ echo 1.2.3-rc.1+44837 | jc --semver -p
+ {
+ "major": 1,
+ "minor": 2,
+ "patch": 3,
+ "prerelease": "rc.1",
+ "build": "44837"
+ }
+
+ $ echo 1.2.3-rc.1+44837 | jc --semver -p -r
+ {
+ "major": "1",
+ "minor": "2",
+ "patch": "3",
+ "prerelease": "rc.1",
+ "build": "44837"
+ }
+
+
+
+### parse
+
+```python
+def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
+```
+
+Main text parsing function
+
+Parameters:
+
+ data: (string) text data to parse
+ raw: (boolean) unprocessed output if True
+ quiet: (boolean) suppress warning messages if True
+
+Returns:
+
+ Dictionary. Raw or processed structured data.
+
+### Parser Information
+Compatibility: linux, darwin, cygwin, win32, aix, freebsd
+
+Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
diff --git a/docs/parsers/sshd_conf.md b/docs/parsers/sshd_conf.md
index e4137fee..7818dd2e 100644
--- a/docs/parsers/sshd_conf.md
+++ b/docs/parsers/sshd_conf.md
@@ -84,6 +84,9 @@ Schema:
],
"ignorerhosts": string,
"ignoreuserknownhosts": string,
+ "include": [
+ string
+ ],
"ipqos": [
string
],
diff --git a/jc/parsers/semver.py b/jc/parsers/semver.py
index 889cbcad..7f6d7903 100644
--- a/jc/parsers/semver.py
+++ b/jc/parsers/semver.py
@@ -13,8 +13,8 @@ Usage (module):
Schema:
-Strings that do not strictly conform to the specification will return a
-blank object.
+Strings that do not strictly conform to the specification will return an
+empty object.
{
"major": integer,
diff --git a/man/jc.1 b/man/jc.1
index 6802010e..bfc9d1f9 100644
--- a/man/jc.1
+++ b/man/jc.1
@@ -700,6 +700,11 @@ PLIST file parser
\fB--rsync-s\fP
`rsync` command streaming parser
+.TP
+.B
+\fB--semver\fP
+Semantic Version string parser
+
.TP
.B
\fB--sfdisk\fP
diff --git a/tests/test_semver.py b/tests/test_semver.py
new file mode 100644
index 00000000..9ad78b35
--- /dev/null
+++ b/tests/test_semver.py
@@ -0,0 +1,101 @@
+import unittest
+import jc.parsers.semver
+
+
+class MyTests(unittest.TestCase):
+ def test_semver_nodata(self):
+ """
+ Test 'semver' with no data
+ """
+ self.assertEqual(jc.parsers.semver.parse('', quiet=True), {})
+
+
+ def test_semver_good_strings(self):
+ good_strings = {
+ '0.0.4': {'major': 0, 'minor': 0, 'patch': 4, 'prerelease': None, 'build': None},
+ '1.2.3': {'major': 1, 'minor': 2, 'patch': 3, 'prerelease': None, 'build': None},
+ '10.20.30': {'major': 10, 'minor': 20, 'patch': 30, 'prerelease': None, 'build': None},
+ '1.1.2-prerelease+meta': {'major': 1, 'minor': 1, 'patch': 2, 'prerelease': 'prerelease', 'build': 'meta'},
+ '1.1.2+meta': {'major': 1, 'minor': 1, 'patch': 2, 'prerelease': None, 'build': 'meta'},
+ '1.1.2+meta-valid': {'major': 1, 'minor': 1, 'patch': 2, 'prerelease': None, 'build': 'meta-valid'},
+ '1.0.0-alpha': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'alpha', 'build': None},
+ '1.0.0-beta': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'beta', 'build': None},
+ '1.0.0-alpha.beta': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'alpha.beta', 'build': None},
+ '1.0.0-alpha.beta.1': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'alpha.beta.1', 'build': None},
+ '1.0.0-alpha.1': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'alpha.1', 'build': None},
+ '1.0.0-alpha0.valid': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'alpha0.valid', 'build': None},
+ '1.0.0-alpha.0valid': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'alpha.0valid', 'build': None},
+ '1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'alpha-a.b-c-somethinglong', 'build': 'build.1-aef.1-its-okay'},
+ '1.0.0-rc.1+build.1': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'rc.1', 'build': 'build.1'},
+ '2.0.0-rc.1+build.123': {'major': 2, 'minor': 0, 'patch': 0, 'prerelease': 'rc.1', 'build': 'build.123'},
+ '1.2.3-beta': {'major': 1, 'minor': 2, 'patch': 3, 'prerelease': 'beta', 'build': None},
+ '10.2.3-DEV-SNAPSHOT': {'major': 10, 'minor': 2, 'patch': 3, 'prerelease': 'DEV-SNAPSHOT', 'build': None},
+ '1.2.3-SNAPSHOT-123': {'major': 1, 'minor': 2, 'patch': 3, 'prerelease': 'SNAPSHOT-123', 'build': None},
+ '1.0.0': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': None, 'build': None},
+ '2.0.0': {'major': 2, 'minor': 0, 'patch': 0, 'prerelease': None, 'build': None},
+ '1.1.7': {'major': 1, 'minor': 1, 'patch': 7, 'prerelease': None, 'build': None},
+ '2.0.0+build.1848': {'major': 2, 'minor': 0, 'patch': 0, 'prerelease': None, 'build': 'build.1848'},
+ '2.0.1-alpha.1227': {'major': 2, 'minor': 0, 'patch': 1, 'prerelease': 'alpha.1227', 'build': None},
+ '1.0.0-alpha+beta': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': 'alpha', 'build': 'beta'},
+ '1.2.3----RC-SNAPSHOT.12.9.1--.12+788': {'major': 1, 'minor': 2, 'patch': 3, 'prerelease': '---RC-SNAPSHOT.12.9.1--.12', 'build': '788'},
+ '1.2.3----R-S.12.9.1--.12+meta': {'major': 1, 'minor': 2, 'patch': 3, 'prerelease': '---R-S.12.9.1--.12', 'build': 'meta'},
+ '1.2.3----RC-SNAPSHOT.12.9.1--.12': {'major': 1, 'minor': 2, 'patch': 3, 'prerelease': '---RC-SNAPSHOT.12.9.1--.12', 'build': None},
+ '1.0.0+0.build.1-rc.10000aaa-kk-0.1': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': None, 'build': '0.build.1-rc.10000aaa-kk-0.1'},
+ '99999999999999999999999.999999999999999999.99999999999999999': {'major': 99999999999999999999999, 'minor': 999999999999999999, 'patch': 99999999999999999, 'prerelease': None, 'build': None},
+ '1.0.0-0A.is.legal': {'major': 1, 'minor': 0, 'patch': 0, 'prerelease': '0A.is.legal', 'build': None}
+ }
+
+ for ver_string, expected in good_strings.items():
+ self.assertEqual(jc.parsers.semver.parse(ver_string, quiet=True), expected)
+
+
+ def test_semver_bad_strings(self):
+ bad_strings = [
+ '1',
+ '1.2',
+ '1.2.3-0123',
+ '1.2.3-0123.0123',
+ '1.1.2+.123',
+ '+invalid',
+ '-invalid',
+ '-invalid+invalid',
+ '-invalid.01',
+ 'alpha',
+ 'alpha.beta',
+ 'alpha.beta.1',
+ 'alpha.1',
+ 'alpha+beta',
+ 'alpha_beta',
+ 'alpha.',
+ 'alpha..',
+ 'beta',
+ '1.0.0-alpha_beta',
+ '-alpha.',
+ '1.0.0-alpha..',
+ '1.0.0-alpha..1',
+ '1.0.0-alpha...1',
+ '1.0.0-alpha....1',
+ '1.0.0-alpha.....1',
+ '1.0.0-alpha......1',
+ '1.0.0-alpha.......1',
+ '01.1.1',
+ '1.01.1',
+ '1.1.01',
+ '1.2',
+ '1.2.3.DEV',
+ '1.2-SNAPSHOT',
+ '1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788',
+ '1.2-RC-SNAPSHOT',
+ '-1.0.3-gamma+b7718',
+ '+justmeta',
+ '9.8.7+meta+meta',
+ '9.8.7-whatever+meta+meta',
+ '99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12'
+ ]
+
+ for item in bad_strings:
+ self.assertEqual(jc.parsers.semver.parse(item, quiet=True), {})
+
+
+if __name__ == '__main__':
+ unittest.main()