1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2026-04-05 17:50:11 +02:00

Compare commits

..

100 Commits

Author SHA1 Message Date
Kelly Brazil
b0d6a7307c Merge pull request #353 from kellyjonbrazil/dev
Dev v1.22.5
2023-01-11 14:39:13 -08:00
Kelly Brazil
53ad793ff8 doc update 2023-01-11 11:17:47 -08:00
Kelly Brazil
ea4332d8e4 add ini-dup tests 2023-01-10 16:06:05 -08:00
Kelly Brazil
5c5ff9324f update multi-line value behavior 2023-01-09 08:31:21 -08:00
Kelly Brazil
2ab6352fdb doc update 2023-01-08 19:54:32 -08:00
Kelly Brazil
56dcbaf40c doc update 2023-01-08 19:53:21 -08:00
Kelly Brazil
029b5abcac add ini-dup parser 2023-01-08 19:46:13 -08:00
Kelly Brazil
32521ac91a doc update 2023-01-08 10:55:48 -08:00
Kelly Brazil
bf88407902 remove special DEFAULT section handling 2023-01-08 10:55:44 -08:00
Kelly Brazil
63cf47db63 doc update 2023-01-06 13:37:34 -08:00
Kelly Brazil
9970866b3b Merge pull request #350 from davemq/arp_aix
AIX ARP support
2023-01-06 13:34:44 -08:00
Dave Marquardt
d61941b276 Merge branch 'dev' into arp_aix 2023-01-06 15:14:39 -06:00
Dave Marquardt
081fdb8026 Updated AIX ARP test comments 2023-01-06 15:13:03 -06:00
Dave Marquardt
f64dfbf79d Update comments about AIX ARP 2023-01-06 15:10:02 -06:00
Dave Marquardt
5fb73f4ad5 Added AIX support for ARP, along with AIX ARP test support 2023-01-06 14:53:50 -06:00
Dave Marquardt
1c16d32420 Added test data for AIX ARP 2023-01-06 14:51:09 -06:00
Kelly Brazil
e367e0d714 doc update 2023-01-06 09:28:13 -08:00
Kelly Brazil
4046649e32 toml tests and doc update 2023-01-06 09:11:00 -08:00
Kelly Brazil
77fcfe439c remove unused list 2023-01-06 08:50:03 -08:00
Kelly Brazil
71d1355419 formatting 2023-01-06 08:36:48 -08:00
Kelly Brazil
d1b270f336 formatting 2023-01-06 08:26:42 -08:00
Dave Marquardt
9190a08332 More AIX permanent ARP entry fixes 2023-01-06 10:25:04 -06:00
Dave Marquardt
3fae50e305 Handle permanent ARP entries on AIX 2023-01-06 10:21:32 -06:00
Dave Marquardt
3582497ed4 Fix interface parsing for incomplete ARP entry on AIX 2023-01-06 10:16:13 -06:00
Dave Marquardt
19a67daabf rewrite check for incomplete ARP entries 2023-01-06 10:12:50 -06:00
Dave Marquardt
02a7e5fd8a Updated ARP incomplete handling for AIX 2023-01-06 10:08:33 -06:00
Dave Marquardt
df72b16022 Start to fix AIX ARP support 2023-01-06 10:00:58 -06:00
Kelly Brazil
61bdc13810 doc update 2023-01-05 17:05:03 -08:00
Kelly Brazil
1350a34316 beautify process function 2023-01-05 16:59:22 -08:00
Kelly Brazil
7cfe68b96a beautify remove_quotes function 2023-01-05 16:55:53 -08:00
Kelly Brazil
7a93a61f54 move unsectioned k/v's to the top level 2023-01-05 15:44:21 -08:00
Kelly Brazil
362977e598 comment unused objects 2023-01-05 14:26:01 -08:00
Kelly Brazil
5cac897beb zipinfo fix for paths with spaces 2023-01-05 12:00:20 -08:00
Kelly Brazil
cad94cc6b3 formatting and doc update 2023-01-05 11:46:14 -08:00
Kelly Brazil
fe4e429e85 Merge pull request #349 from davemq/mount_aix
Add AIX mount support
2023-01-05 11:39:42 -08:00
Kelly Brazil
d83b10e2d5 doc update 2023-01-05 11:27:49 -08:00
Kelly Brazil
bd2a757fcd more refactoring 2023-01-05 11:27:00 -08:00
Dave Marquardt
358b69a4cb Merge branch 'dev' into mount_aix 2023-01-05 11:06:00 -06:00
Dave Marquardt
08821a1454 Removed unneeded imports from mount parser 2023-01-05 10:57:23 -06:00
Kelly Brazil
90277c1d87 formatting 2023-01-05 08:54:40 -08:00
Dave Marquardt
a51e702f77 Added AIX mount test 2023-01-05 10:53:02 -06:00
Dave Marquardt
466e37128b Merge branch 'dev' into mount_aix 2023-01-05 10:32:47 -06:00
Dave Marquardt
f1ea61388f Added AIX mount support 2023-01-05 10:28:01 -06:00
Kelly Brazil
72c11fda3a more refactoring 2023-01-05 08:00:18 -08:00
Kelly Brazil
d6645983ef more refactoring 2023-01-04 20:54:31 -08:00
Kelly Brazil
01e911ecdb remove more type annotations 2023-01-04 19:17:29 -08:00
Kelly Brazil
12c0b3d889 remove more type annotations 2023-01-04 19:13:12 -08:00
Kelly Brazil
c1075e40b7 remove more type hinting for python 3.6 2023-01-04 19:09:41 -08:00
Kelly Brazil
fabe3f01b7 remove type annotations for python 3.6 compatibility 2023-01-04 19:00:49 -08:00
Kelly Brazil
3a2ff61899 fix lsusb for extra hub port info and add videocontrol and videostreaming sections 2023-01-04 18:27:51 -08:00
Kelly Brazil
aeff94d272 add toml parser 2023-01-04 18:26:13 -08:00
Kelly Brazil
c94f5d83fa fix kv and ini parsers to only remove 1 quote from beginning and end 2023-01-03 13:26:52 -08:00
Kelly Brazil
b0756fa0f9 doc update 2023-01-03 13:05:51 -08:00
Kelly Brazil
6c85abd57b doc update 2023-01-03 13:04:13 -08:00
Kelly Brazil
7659ae94bd formatting 2023-01-03 12:59:10 -08:00
Kelly Brazil
e306e81e43 formatting 2023-01-03 12:57:04 -08:00
Kelly Brazil
6d5768b26b simplify code and doc update 2023-01-03 12:55:38 -08:00
Kelly Brazil
cee9f8bf32 remove duplicate code. add tests 2023-01-03 12:44:12 -08:00
Kelly Brazil
b8e2678c01 doc update 2023-01-03 12:30:23 -08:00
Kelly Brazil
b0fe96ed03 doc update 2023-01-03 12:28:26 -08:00
Kelly Brazil
bb65ec380b separate kv and ini parsers. add fake top-level section in ini files that are missing it. 2023-01-03 12:11:05 -08:00
Kelly Brazil
ae6248227b version bump 2023-01-03 11:53:57 -08:00
Kelly Brazil
36ce3c791d Merge pull request #343 from kellyjonbrazil/dev
Dev v1.22.4
2022-12-30 13:29:57 -08:00
Kelly Brazil
5d639db5a8 Merge branch 'master' into dev 2022-12-30 13:24:24 -08:00
Kelly Brazil
63161ffdbb doc update 2022-12-30 13:11:54 -08:00
Kelly Brazil
cf11c71831 move import to except block 2022-12-30 11:53:09 -08:00
Kelly Brazil
048f8c1f4b formatting 2022-12-30 11:49:20 -08:00
Kelly Brazil
b6b42d9071 doc update 2022-12-30 11:46:40 -08:00
Kelly Brazil
d43863ee9f delete the tempfile after use 2022-12-30 11:32:14 -08:00
Kelly Brazil
177d10577f create NamedTemporaryFile with delete=False for Windows compatibilty 2022-12-30 11:17:52 -08:00
Kelly Brazil
47d8e163de patch for windows compatibility: can only open tempfile once 2022-12-30 11:06:45 -08:00
Kelly Brazil
e49c621e59 try writing to file as bytes for windows compatibility 2022-12-30 10:44:46 -08:00
Kelly Brazil
5e32a6d828 fix for python 3.10+ compatibility 2022-12-30 10:37:12 -08:00
Kelly Brazil
b1358a7eca add nextstep plist support. using the pbPlist library 2022-12-30 10:27:03 -08:00
Kelly Brazil
ba89092e12 formatting 2022-12-29 14:52:33 -08:00
Kelly Brazil
f855420a82 doc update 2022-12-29 09:25:31 -08:00
Kelly Brazil
1589a81945 fix issue with text serial numbers in certificates 2022-12-28 21:59:38 -08:00
Kelly Brazil
eec2583cb2 formatting 2022-12-27 15:59:43 -08:00
Kelly Brazil
481c6a8de2 doc update 2022-12-27 14:01:03 -08:00
Kelly Brazil
68717e8493 add parser metadata tags and category view to help 2022-12-27 13:59:10 -08:00
Kelly Brazil
c524d37136 use ubuntu-20.04 instead of latest for python 3.6 2022-12-23 10:25:12 -08:00
Kelly Brazil
6674f549f2 version bumps and doc update 2022-12-23 10:14:19 -08:00
Kelly Brazil
ba371ec730 fix proc signature for pid_stat so hack comm names with parenthesis are handled correctly. 2022-12-23 10:08:19 -08:00
Kelly Brazil
44d1d52426 fix test for python 3.6 2022-12-23 09:40:26 -08:00
Kelly Brazil
edcca4fb96 remove re type annotations for python 3.6 compatibility 2022-12-23 09:23:51 -08:00
Kelly Brazil
6fbf9458d1 Merge branch 'dev' of https://github.com/kellyjonbrazil/jc into dev 2022-12-23 09:21:29 -08:00
Kelly Brazil
a476e4639b force test 2022-12-23 09:21:27 -08:00
Kelly Brazil
5534a1c755 change to ubuntu-20.04 for python 3.6 tests 2022-12-23 09:20:32 -08:00
Kelly Brazil
c45518db70 Merge branch 'dev' of https://github.com/kellyjonbrazil/jc into dev 2022-12-23 09:17:44 -08:00
Kelly Brazil
55480c059a python 3.6 compatibility: remove re.Match type annotation 2022-12-23 09:17:42 -08:00
Kelly Brazil
04a2af8681 add python 3.6 to tests 2022-12-23 09:14:19 -08:00
Kelly Brazil
71f672a0e0 add python 3.6 to tests 2022-12-23 09:13:00 -08:00
Kelly Brazil
33c40874fa version bump and add iwconfig parser 2022-12-23 09:12:09 -08:00
Kelly Brazil
b3ced64672 Merge pull request #334 from vrince/iwconfig-parser
add new iwconfig parser
2022-12-23 09:00:22 -08:00
Thomas Vincent
a60bf00d54 iwconfig: add test with many interface 2022-12-21 17:25:57 -05:00
Thomas Vincent
8d9a2744af iwconfig: add example in the docstring 2022-12-21 17:15:25 -05:00
Thomas Vincent
bcd7bac950 iwconfig: add to parser list + move test fixture to generic folder 2022-12-21 17:12:20 -05:00
Thomas Vincent
e996b3a9f6 add iwconfig raw vs process + tests 2022-12-19 22:36:48 -05:00
Thomas Vincent
e207cccdc5 fix copy/paste mess in regex 2022-12-19 22:23:10 -05:00
Thomas Vincent
9776dd1082 add new iwconfig parser 2022-12-19 22:13:58 -05:00
279 changed files with 10649 additions and 492 deletions

View File

@@ -13,8 +13,8 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
os: [macos-latest, ubuntu-20.04, windows-latest]
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v3

View File

@@ -1,5 +1,28 @@
jc changelog
20230111 v1.22.5
- Add TOML file parser
- Add INI with duplicate key support file parser
- Add AIX support for the `arp` command parser
- Add AIX support for the `mount` command parser
- Fix `lsusb` command parser when extra hub port status information is output
- Refactor `lsusb` command parser for more code reuse
- Fix INI file parser to include top-level values with no section header
- Fix INI file parser to not specially handle the [DEFAULT] section
- Fix INI file and Key/Value parsers to only remove one quotation mark from the
beginning and end of values.
- Update copyright dates
20221230 v1.22.4
- Add `iwconfig` command parser
- Add NeXTSTEP format support to the PLIST file parser
- Fix `proc` parser magic signature detection for `/proc/pid/stat` hacks
- Fix `x509-cert` parser for string serial numbers
- Add category tags to parser metadata: generic, standard, file, string, binary, command
- Add "list parsers by category" view to help
- Fix python 3.6-related issues
- Add python 3.6 to automated tests
20221216 v1.22.3
- Add Common Log Format and Combined Log Format file parser (standard and streaming)
- Add PostgreSQL password file parser

View File

@@ -1636,21 +1636,21 @@ cat example.ini | jc --ini -p
```
```json
{
"bitbucket.org": {
"ServeraLiveInterval": "45",
"DEFAULT": {
"ServerAliveInterval": "45",
"Compression": "yes",
"CompressionLevel": "9",
"ForwardX11": "yes",
"ForwardX11": "yes"
},
"bitbucket.org": {
"User": "hg"
},
"topsecret.server.com": {
"ServeraLiveInterval": "45",
"Compression": "yes",
"CompressionLevel": "9",
"ForwardX11": "no",
"Port": "50022"
"Port": "50022",
"ForwardX11": "no"
}
}
```
### iostat
```bash

View File

@@ -201,15 +201,17 @@ option.
| ` --id` | `id` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/id) |
| ` --ifconfig` | `ifconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ifconfig) |
| ` --ini` | INI file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini) |
| ` --ini-dup` | INI with duplicate key file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini_dup) |
| ` --iostat` | `iostat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat) |
| ` --iostat-s` | `iostat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat_s) |
| ` --ip-address` | IPv4 and IPv6 Address string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_address) |
| ` --iptables` | `iptables` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iptables) |
| ` --iw-scan` | `iw dev [device] scan` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iw_scan) |
| ` --iwconfig` | `iwconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iwconfig) |
| ` --jar-manifest` | Java MANIFEST.MF file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jar_manifest) |
| ` --jobs` | `jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jobs) |
| ` --jwt` | JWT string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jwt) |
| ` --kv` | Key/Value file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/kv) |
| ` --kv` | Key/Value file and string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/kv) |
| ` --last` | `last` and `lastb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/last) |
| ` --ls` | `ls` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls) |
| ` --ls-s` | `ls` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls_s) |
@@ -265,6 +267,7 @@ option.
| ` --time` | `/usr/bin/time` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/time) |
| ` --timedatectl` | `timedatectl status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timedatectl) |
| ` --timestamp` | Unix Epoch Timestamp string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timestamp) |
| ` --toml` | TOML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/toml) |
| ` --top` | `top -b` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top) |
| ` --top-s` | `top -b` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top_s) |
| ` --tracepath` | `tracepath` and `tracepath6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/tracepath) |
@@ -755,37 +758,31 @@ ifconfig | jc -p --ifconfig # or: jc -p ifconfig
cat example.ini
```
```
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
foo = fiz
bar = buz
[bitbucket.org]
User = hg
[section1]
fruit = apple
color = blue
[topsecret.server.com]
Port = 50022
ForwardX11 = no
[section2]
fruit = pear
color = green
```
```bash
cat example.ini | jc -p --ini
```
```json
{
"bitbucket.org": {
"ServeraLiveInterval": "45",
"Compression": "yes",
"CompressionLevel": "9",
"ForwardX11": "yes",
"User": "hg"
"foo": "fiz",
"bar": "buz",
"section1": {
"fruit": "apple",
"color": "blue"
},
"topsecret.server.com": {
"ServeraLiveInterval": "45",
"Compression": "yes",
"CompressionLevel": "9",
"ForwardX11": "no",
"Port": "50022"
"section2": {
"fruit": "pear",
"color": "green"
}
}
```
@@ -1241,4 +1238,4 @@ cat istio.yaml | jc -p --yaml
]
```
© 2019-2022 Kelly Brazil
© 2019-2023 Kelly Brazil

View File

@@ -3,8 +3,8 @@ _jc()
local cur prev words cword jc_commands jc_parsers jc_options \
jc_about_options jc_about_mod_options jc_help_options jc_special_options
jc_commands=(acpi airport arp blkid cbt 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 os-prober 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 --cbt --cef --cef-s --chage --cksum --clf --clf-s --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 --git-ls-remote --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 --openvpn --os-prober --passwd --pci-ids --pgpass --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_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober 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 --cbt --cef --cef-s --chage --cksum --clf --clf-s --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 --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --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 --toml --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)

View File

@@ -9,7 +9,7 @@ _jc() {
jc_help_options jc_help_options_describe \
jc_special_options jc_special_options_describe
jc_commands=(acpi airport arp blkid cbt 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 os-prober 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_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober 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_commands_describe=(
'acpi:run "acpi" command with magic syntax.'
'airport:run "airport" command with magic syntax.'
@@ -38,6 +38,7 @@ _jc() {
'iostat:run "iostat" command with magic syntax.'
'iptables:run "iptables" command with magic syntax.'
'iw:run "iw" command with magic syntax.'
'iwconfig:run "iwconfig" command with magic syntax.'
'jobs:run "jobs" command with magic syntax.'
'last:run "last" command with magic syntax.'
'lastb:run "lastb" command with magic syntax.'
@@ -101,7 +102,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 --cbt --cef --cef-s --chage --cksum --clf --clf-s --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 --git-ls-remote --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 --openvpn --os-prober --passwd --pci-ids --pgpass --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=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cbt --cef --cef-s --chage --cksum --clf --clf-s --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 --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --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 --toml --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'
@@ -150,15 +151,17 @@ _jc() {
'--id:`id` command parser'
'--ifconfig:`ifconfig` command parser'
'--ini:INI file parser'
'--ini-dup:INI with duplicate key file parser'
'--iostat:`iostat` command parser'
'--iostat-s:`iostat` command streaming parser'
'--ip-address:IPv4 and IPv6 Address string parser'
'--iptables:`iptables` command parser'
'--iw-scan:`iw dev [device] scan` command parser'
'--iwconfig:`iwconfig` command parser'
'--jar-manifest:Java MANIFEST.MF file parser'
'--jobs:`jobs` command parser'
'--jwt:JWT string parser'
'--kv:Key/Value file parser'
'--kv:Key/Value file and string parser'
'--last:`last` and `lastb` command parser'
'--ls:`ls` command parser'
'--ls-s:`ls` command streaming parser'
@@ -263,6 +266,7 @@ _jc() {
'--time:`/usr/bin/time` command parser'
'--timedatectl:`timedatectl status` command parser'
'--timestamp:Unix Epoch Timestamp string parser'
'--toml:TOML file parser'
'--top:`top -b` command parser'
'--top-s:`top -b` command streaming parser'
'--tracepath:`tracepath` and `tracepath6` command parser'
@@ -289,8 +293,8 @@ _jc() {
)
jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --unbuffer -u --yaml-out -y)
jc_options_describe=(
'--force-color:force color output even when using pipes (overrides -m)'
'-C:force color output even when using pipes (overrides -m)'
'--force-color:force color output (overrides -m)'
'-C:force color output (overrides -m)'
'--debug:debug (double for verbose debug)'
'-d:debug (double for verbose debug)'
'--monochrome:monochrome output'
@@ -321,8 +325,8 @@ _jc() {
'-y:YAML output'
'--monochrome:monochrome output'
'-m:monochrome output'
'--force-color:force color output even when using pipes (overrides -m)'
'-C:force color output even when using pipes (overrides -m)'
'--force-color:force color output (overrides -m)'
'-C:force color output (overrides -m)'
)
jc_help_options=(--help -h)
jc_help_options_describe=(

View File

@@ -140,4 +140,4 @@ Returns:
### Parser Information
Compatibility: linux, aix, freebsd, darwin
Version 1.11 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.12 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -114,4 +114,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -240,4 +240,4 @@ Returns:
### Parser Information
Compatibility: linux, aix, freebsd, darwin
Version 2.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 2.2 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -3,13 +3,15 @@
# jc.parsers.ini
jc - JSON Convert `INI` file parser
jc - JSON Convert INI file parser
Parses standard `INI` files and files containing simple key/value pairs.
Parses standard INI files.
- Delimiter can be `=` or `:`. Missing values are supported.
- Comment prefix can be `#` or `;`. Comments must be on their own line.
- If duplicate keys are found, only the last value will be used.
- If any section names have the same name as a top-level key, the top-level
key will be overwritten by the section data.
> Note: Values starting and ending with double or single quotation marks
> will have the marks removed. If you would like to keep the quotation
@@ -27,45 +29,47 @@ Usage (module):
Schema:
ini or key/value document converted to a dictionary - see the configparser
INI document converted to a dictionary - see the python configparser
standard library documentation for more details.
{
"key1": string,
"key2": string
"<key1>": string,
"<key2>": string,
"<section1>": {
"<key1>": string,
"<key2>": string
},
"<section2>": {
"<key1>": string,
"<key2>": string
}
}
Examples:
$ cat example.ini
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
foo = fiz
bar = buz
[bitbucket.org]
User = hg
[section1]
fruit = apple
color = blue
[topsecret.server.com]
Port = 50022
ForwardX11 = no
[section2]
fruit = pear
color = green
$ cat example.ini | jc --ini -p
{
"bitbucket.org": {
"ServerAliveInterval": "45",
"Compression": "yes",
"CompressionLevel": "9",
"ForwardX11": "yes",
"User": "hg"
"foo": "fiz",
"bar": "buz",
"section1": {
"fruit": "apple",
"color": "blue"
},
"topsecret.server.com": {
"ServerAliveInterval": "45",
"Compression": "yes",
"CompressionLevel": "9",
"ForwardX11": "no",
"Port": "50022"
"section2": {
"fruit": "pear",
"color": "green"
}
}
@@ -87,9 +91,9 @@ Parameters:
Returns:
Dictionary representing the ini file
Dictionary representing the INI file.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 2.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

121
docs/parsers/ini_dup.md Normal file
View File

@@ -0,0 +1,121 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.ini_dup"></a>
# jc.parsers.ini\_dup
jc - JSON Convert INI with duplicate key file parser
Parses standard INI files and preserves duplicate values. All values are
contained in lists/arrays.
- Delimiter can be `=` or `:`. Missing values are supported.
- Comment prefix can be `#` or `;`. Comments must be on their own line.
- If any section names have the same name as a top-level key, the top-level
key will be overwritten by the section data.
- If multi-line values are used, each line will be a separate item in the
value list. Blank lines in multi-line values are not supported.
> Note: Values starting and ending with double or single quotation marks
> will have the marks removed. If you would like to keep the quotation
> marks, use the `-r` command-line argument or the `raw=True` argument in
> `parse()`.
Usage (cli):
$ cat foo.ini | jc --ini
Usage (module):
import jc
result = jc.parse('ini', ini_file_output)
Schema:
INI document converted to a dictionary - see the python configparser
standard library documentation for more details.
{
"<key1>": [
string
],
"<key2>": [
string
],
"<section1>": {
"<key1>": [
string
],
"<key2>": [
string
]
}
}
Examples:
$ cat example.ini
foo = fiz
bar = buz
[section1]
fruit = apple
color = blue
color = red
[section2]
fruit = pear
fruit = peach
color = green
$ cat example.ini | jc --ini -p
{
"foo": [
"fiz"
],
"bar": [
"buz"
],
"section1": {
"fruit": [
"apple"
],
"color": [
"blue",
"red"
]
},
"section2": {
"fruit": [
"pear",
"peach"
],
"color": [
"green"
]
}
}
<a id="jc.parsers.ini_dup.parse"></a>
### parse
```python
def parse(data, raw=False, quiet=False)
```
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 representing the INI file.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

111
docs/parsers/iwconfig.md Normal file
View File

@@ -0,0 +1,111 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.iwconfig"></a>
# jc.parsers.iwconfig
jc - JSON Convert `iwconfig` command output parser
No `iwconfig` options are supported.
Usage (cli):
$ iwconfig | jc --iwconfig
or
$ jc iwconfig
Usage (module):
import jc
result = jc.parse('iwconfig', iwconfig_command_output)
Schema:
[
{
"name": string,
"protocol": string,
"essid": string,
"mode": string,
"frequency": float,
"frequency_unit": string,
"access_point": string,
"bit_rate": float,
"bit_rate_unit": string,
"tx_power": integer,
"tx_power_unit": string,
"retry_short_limit": integer,
"rts_threshold": boolean,
"fragment_threshold": boolean,
"power_management": boolean,
"link_quality": string,
"signal_level": integer,
"signal_level_unit": string,
"rx_invalid_nwid": integer,
"rx_invalid_crypt": integer,
"rx_invalid_frag": integer,
"tx_excessive_retries": integer,
"invalid_misc": integer,
"missed_beacon": integer
}
]
Examples:
$ iwconfig | jc --iwconfig -p
[
{
"name": "wlp5s0",
"protocol": "IEEE 802.11",
"essid": "BLABLABLA",
"mode": "Managed",
"frequency": 5.18,
"frequency_unit": "GHz",
"access_point": "E6:64:DA:16:51:BF",
"bit_rate": 6.0,
"bit_rate_unit": "Mb/s",
"tx_power": 30,
"tx_power_unit": "dBm",
"retry_short_limit": 7,
"rts_threshold": false,
"fragment_threshold": false,
"power_management": true,
"link_quality": "61/70",
"signal_level": -49,
"signal_level_unit": "dBm",
"rx_invalid_nwid": 0,
"rx_invalid_crypt": 0,
"rx_invalid_frag": 0,
"tx_excessive_retries": 0,
"invalid_misc": 2095,
"missed_beacon": 0
}
]
<a id="jc.parsers.iwconfig.parse"></a>
### parse
```python
def parse(data: str,
raw: bool = False,
quiet: bool = False) -> List[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:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux
Version 1.0 by Thomas Vincent (vrince@gmail.com)

View File

@@ -3,7 +3,7 @@
# jc.parsers.kv
jc - JSON Convert `Key/Value` file parser
jc - JSON Convert `Key/Value` file and string parser
Supports files containing simple key/value pairs.
@@ -26,8 +26,8 @@ Usage (module):
Schema:
key/value document converted to a dictionary - see the configparser standard
library documentation for more details.
Key/Value document converted to a dictionary - see the python configparser
standard library documentation for more details.
{
"key1": string,
@@ -41,6 +41,7 @@ Examples:
name = John Doe
address=555 California Drive
age: 34
; comments can include # or ;
# delimiter can be = or :
# quoted values have quotation marks stripped by default
@@ -65,8 +66,6 @@ def parse(data, raw=False, quiet=False)
Main text parsing function
Note: this is just a wrapper for jc.parsers.ini
Parameters:
data: (string) text data to parse
@@ -75,9 +74,9 @@ Parameters:
Returns:
Dictionary representing the key/value file
Dictionary representing a Key/Value pair document.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 2.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -102,6 +102,28 @@ Schema:
]
}
},
"videocontrol_descriptors": [
{
"<item>": {
"value": string,
"description": string,
"attributes": [
string
]
}
}
],
"videostreaming_descriptors": [
{
"<item>": {
"value": string,
"description": string,
"attributes": [
string
]
}
}
],
"endpoint_descriptors": [
{
"<item>": {
@@ -290,4 +312,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -25,7 +25,7 @@ Schema:
"filesystem": string,
"mount_point": string,
"type": string,
"access": [
"options": [
string
]
}
@@ -39,7 +39,7 @@ Example:
"filesystem": "sysfs",
"mount_point": "/sys",
"type": "sysfs",
"access": [
"options": [
"rw",
"nosuid",
"nodev",
@@ -51,7 +51,7 @@ Example:
"filesystem": "proc",
"mount_point": "/proc",
"type": "proc",
"access": [
"options": [
"rw",
"nosuid",
"nodev",
@@ -63,7 +63,7 @@ Example:
"filesystem": "udev",
"mount_point": "/dev",
"type": "devtmpfs",
"access": [
"options": [
"rw",
"nosuid",
"relatime",
@@ -96,6 +96,6 @@ Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, freebsd
Compatibility: linux, darwin, freebsd, aix
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -5,7 +5,7 @@
jc - JSON Convert PLIST file parser
Converts binary and XML PLIST files.
Converts binary, XML, and NeXTSTEP PLIST files.
Binary values are converted into an ASCII hex representation.
@@ -69,9 +69,9 @@ Parameters:
Returns:
List of Dictionaries. Raw or processed structured data.
Dictionary. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -139,4 +139,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

81
docs/parsers/toml.md Normal file
View File

@@ -0,0 +1,81 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.toml"></a>
# jc.parsers.toml
jc - JSON Convert TOML file parser
Usage (cli):
$ cat file.toml | jc --toml
Usage (module):
import jc
result = jc.parse('toml', toml_file_output)
Schema:
TOML Document converted to a Dictionary.
See https://toml.io/en/ for details.
{
"key1": string/int/float/boolean/null/array/object,
"key2": string/int/float/boolean/null/array/object
}
Examples:
$ cat file.toml
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00
[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
$ cat file.toml | jc --toml -p
{
"title": "TOML Example",
"owner": {
"name": "Tom Preston-Werner",
"dob": 296667120,
"dob_iso": "1979-05-27T07:32:00-08:00"
},
"database": {
"enabled": true,
"ports": [
8000,
8001,
8002
]
}
}
<a id="jc.parsers.toml.parse"></a>
### 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)

View File

@@ -32,6 +32,7 @@ Schema:
"tbs_certificate": {
"version": string,
"serial_number": string, # [0]
"serial_number_str": string,
"signature": {
"algorithm": string,
"parameters": string/null,
@@ -43,7 +44,9 @@ Schema:
"organization_name": array/string,
"organizational_unit_name": array/string,
"common_name": string,
"email_address": string
"email_address": string,
"serial_number": string, # [0]
"serial_number_str": string
},
"validity": {
"not_before": integer, # [1]
@@ -58,7 +61,9 @@ Schema:
"organization_name": array/string,
"organizational_unit_name": array/string,
"common_name": string,
"email_address": string
"email_address": string,
"serial_number": string, # [0]
"serial_number_str": string
},
"subject_public_key_info": {
"algorithm": {
@@ -428,4 +433,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -18,8 +18,8 @@ Usage (module):
Schema:
YAML Document converted to a Dictionary
See https://pypi.org/project/ruamel.yaml for details
YAML Document converted to a Dictionary.
See https://pypi.org/project/ruamel.yaml for details.
[
{
@@ -30,7 +30,7 @@ Schema:
Examples:
$ cat istio-mtls-permissive.yaml
$ cat file.yaml
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
@@ -51,7 +51,7 @@ Examples:
tls:
mode: ISTIO_MUTUAL
$ cat istio-mtls-permissive.yaml | jc --yaml -p
$ cat file.yaml | jc --yaml -p
[
{
"apiVersion": "authentication.istio.io/v1alpha1",

View File

@@ -99,4 +99,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin
Version 1.1 by Matt J (https://github.com/listuser)
Version 1.2 by Matt J (https://github.com/listuser)

View File

@@ -9,7 +9,7 @@ from datetime import datetime, timezone
import textwrap
import shlex
import subprocess
from typing import List, Union, Optional, TextIO
from typing import List, Dict, Union, Optional, TextIO
from types import ModuleType
from .lib import (
__version__, parser_info, all_parser_info, parsers, _get_parser, _parser_is_streaming,
@@ -48,7 +48,7 @@ class info():
author: str = 'Kelly Brazil'
author_email: str = 'kellyjonbrazil@gmail.com'
website: str = 'https://github.com/kellyjonbrazil/jc'
copyright: str = '© 2019-2022 Kelly Brazil'
copyright: str = '© 2019-2023 Kelly Brazil'
license: str = 'MIT License'
@@ -64,12 +64,12 @@ if PYGMENTS_INSTALLED:
class JcCli():
__slots__ = (
'data_in', 'data_out', 'options', 'args', 'parser_module', 'parser_name', 'indent', 'pad',
'custom_colors', 'show_hidden', 'ascii_only', 'json_separators', 'json_indent',
'run_timestamp', 'about', 'debug', 'verbose_debug', 'force_color', 'mono', 'help_me',
'pretty', 'quiet', 'ignore_exceptions', 'raw', 'meta_out', 'unbuffer', 'version_info',
'yaml_output', 'bash_comp', 'zsh_comp', 'magic_found_parser', 'magic_options',
'magic_run_command', 'magic_run_command_str', 'magic_stdout', 'magic_stderr',
'magic_returncode'
'custom_colors', 'show_hidden', 'show_categories', 'ascii_only', 'json_separators',
'json_indent', 'run_timestamp', 'about', 'debug', 'verbose_debug', 'force_color', 'mono',
'help_me', 'pretty', 'quiet', 'ignore_exceptions', 'raw', 'meta_out', 'unbuffer',
'version_info', 'yaml_output', 'bash_comp', 'zsh_comp', 'magic_found_parser',
'magic_options', 'magic_run_command', 'magic_run_command_str', 'magic_stdout',
'magic_stderr', 'magic_returncode'
)
def __init__(self) -> None:
@@ -83,6 +83,7 @@ class JcCli():
self.pad: int = 0
self.custom_colors: CustomColorType = {}
self.show_hidden: bool = False
self.show_categories: bool = False
self.ascii_only: bool = False
self.json_separators: Optional[tuple[str, str]] = (',', ':')
self.json_indent: Optional[int] = None
@@ -198,6 +199,41 @@ class JcCli():
return ptext
def parser_categories_text(self) -> str:
"""Return lists of parsers by category"""
category_text: str = ''
padding_char: str = ' '
all_parsers = all_parser_info(show_hidden=True, show_deprecated=False)
generic = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'generic' in x['tags']]
standard = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'standard' in x['tags']]
command = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'command' in x['tags']]
file_str_bin = [
{'arg': x['argument'], 'desc': x['description']} for x in all_parsers
if 'file' in x['tags'] or
'string' in x['tags'] or
'binary' in x['tags']
]
streaming = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if x.get('streaming')]
categories: Dict = {
'Generic Parsers:': generic,
'Standard Spec Parsers:': standard,
'File/String/Binary Parsers:': file_str_bin,
'Streaming Parsers:': streaming,
'Command Parsers:': command
}
for cat, cat_objs in categories.items():
category_text += f'{cat} ({len(cat_objs)})\n'
for p in cat_objs:
parser_arg: str = p.get('arg', 'UNKNOWN')
parser_desc: str = p.get('desc', 'No description available.')
padding: int = self.pad - len(parser_arg)
padding_text: str = padding_char * padding
category_text += f'{parser_arg}{padding_text}{parser_desc}\n'
category_text += '\n'
return category_text[:-1]
def options_text(self) -> str:
"""Return the argument and description information from each option"""
otext: str = ''
@@ -236,8 +272,6 @@ class JcCli():
def helptext(self) -> str:
"""Return the help text with the list of parsers"""
self.indent = 4
self.pad = 20
parsers_string: str = self.parsers_text()
options_string: str = self.options_text()
helptext_string: str = f'{helptext_preamble_string}{parsers_string}\nOptions:\n{options_string}\n{helptext_end_string}'
@@ -248,6 +282,13 @@ class JcCli():
Pages the parser documentation if a parser is found in the arguments,
otherwise the general help text is printed.
"""
self.indent = 4
self.pad = 22
if self.show_categories:
utils._safe_print(self.parser_categories_text())
return
for arg in self.args:
parser_name: str = self.parser_shortname(arg)
@@ -655,6 +696,7 @@ class JcCli():
self.force_color = 'C' in self.options
self.help_me = 'h' in self.options
self.show_hidden = self.options.count('h') > 1 # verbose help
self.show_categories = self.options.count('h') > 2
self.pretty = 'p' in self.options
self.quiet = 'q' in self.options
self.ignore_exceptions = self.options.count('q') > 1

View File

@@ -3,7 +3,7 @@ from typing import List, Dict
long_options_map: Dict[str, List[str]] = {
'--about': ['a', 'about jc'],
'--force-color': ['C', 'force color output even when using pipes (overrides -m)'],
'--force-color': ['C', 'force color output (overrides -m)'],
'--debug': ['d', 'debug (double for verbose debug)'],
'--help': ['h', 'help (--help --parser_name for parser documentation)'],
'--monochrome': ['m', 'monochrome output'],
@@ -91,6 +91,7 @@ Examples:
Parser Documentation:
$ jc --help --dig
Show Hidden Parsers:
$ jc -hh
More Help:
$ jc -hh # show hidden parsers
$ jc -hhh # list parsers by category tags
'''

View File

@@ -20,6 +20,7 @@ if sys.version_info >= (3, 8):
"author_email": str,
"compatible": List[str],
"magic_commands": List[str],
"tags": List[str],
"documentation": str,
"streaming": bool,
"plugin": bool,

View File

@@ -9,7 +9,7 @@ from .jc_types import ParserInfoType, JSONDictType
from jc import appdirs
__version__ = '1.22.3'
__version__ = '1.22.5'
parsers: List[str] = [
'acpi',
@@ -59,12 +59,14 @@ parsers: List[str] = [
'id',
'ifconfig',
'ini',
'ini-dup',
'iostat',
'iostat-s',
'ip-address',
'iptables',
'iso-datetime',
'iw-scan',
'iwconfig',
'jar-manifest',
'jobs',
'jwt',
@@ -173,6 +175,7 @@ parsers: List[str] = [
'time',
'timedatectl',
'timestamp',
'toml',
'top',
'top-s',
'tracepath',

View File

@@ -233,6 +233,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['acpi']
tags = ['command']
__version__ = info.version

View File

@@ -86,6 +86,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['darwin']
magic_commands = ['airport -I']
tags = ['command']
__version__ = info.version

View File

@@ -115,6 +115,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['darwin']
magic_commands = ['airport -s']
tags = ['command']
__version__ = info.version

View File

@@ -119,12 +119,13 @@ import jc.parsers.universal
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.11'
version = '1.12'
description = '`arp` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'aix', 'freebsd', 'darwin']
magic_commands = ['arp']
tags = ['command']
__version__ = info.version
@@ -221,14 +222,26 @@ def parse(
else:
for line in cleandata:
splitline = line.split()
if '<incomplete>' not in splitline:
# Ignore AIX bucket information
if 'bucket:' in splitline[0]:
continue
elif 'There' in splitline[0] and 'are' in splitline[1]:
continue
# AIX uses (incomplete)
elif '<incomplete>' not in splitline and '(incomplete)' not in splitline:
output_line = {
'name': splitline[0],
'address': splitline[1].lstrip('(').rstrip(')'),
'hwtype': splitline[4].lstrip('[').rstrip(']'),
'hwaddress': splitline[3],
'iface': splitline[6],
}
# Handle permanence and ignore interface in AIX
if 'permanent' in splitline:
output_line['permanent'] = True
elif 'in' not in splitline[6]: # AIX doesn't show interface
output_line['iface'] = splitline[6]
else:
output_line = {
@@ -236,8 +249,10 @@ def parse(
'address': splitline[1].lstrip('(').rstrip(')'),
'hwtype': None,
'hwaddress': None,
'iface': splitline[5],
}
# AIX doesn't show interface
if len(splitline) >= 5:
output_line['iface'] = splitline[5]
raw_output.append(output_line)

View File

@@ -130,6 +130,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['generic', 'string']
__version__ = info.version

View File

@@ -115,6 +115,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['generic', 'string']
__version__ = info.version

View File

@@ -127,6 +127,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['blkid']
tags = ['command']
__version__ = info.version

View File

@@ -106,6 +106,7 @@ class info():
author_email = 'andreas.weiden@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['cbt']
tags = ['command']
__version__ = info.version

View File

@@ -129,6 +129,8 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the pycef library at https://github.com/DavidJBianco/pycef/releases/tag/v1.11-2'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'file', 'string']
__version__ = info.version

View File

@@ -103,6 +103,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the pycef library at https://github.com/DavidJBianco/pycef/releases/tag/v1.11-2'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'file', 'string']
streaming = True

View File

@@ -63,6 +63,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['chage --list', 'chage -l']
tags = ['command']
__version__ = info.version

View File

@@ -60,6 +60,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['cksum', 'sum']
tags = ['command']
__version__ = info.version

View File

@@ -179,6 +179,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'file', 'string']
__version__ = info.version

View File

@@ -95,6 +95,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'file', 'string']
streaming = True

View File

@@ -180,6 +180,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'aix', 'freebsd']
magic_commands = ['crontab']
tags = ['file', 'command']
__version__ = info.version

View File

@@ -176,6 +176,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'aix', 'freebsd']
tags = ['file', 'command']
__version__ = info.version

View File

@@ -86,6 +86,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the python standard csv library'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'file', 'string']
__version__ = info.version

View File

@@ -69,6 +69,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the python standard csv library'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'file', 'string']
streaming = True

View File

@@ -84,6 +84,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'freebsd']
magic_commands = ['date']
tags = ['command']
__version__ = info.version

View File

@@ -75,6 +75,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the pyiso8601 library from https://github.com/micktwomey/pyiso8601/releases/tag/1.0.2'
compatible = ['linux', 'aix', 'freebsd', 'darwin', 'win32', 'cygwin']
tags = ['standard', 'string']
__version__ = info.version

View File

@@ -105,6 +105,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'freebsd']
magic_commands = ['df']
tags = ['command']
__version__ = info.version

View File

@@ -328,6 +328,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'aix', 'freebsd', 'darwin', 'win32', 'cygwin']
magic_commands = ['dig']
tags = ['command']
__version__ = info.version

View File

@@ -126,6 +126,7 @@ class info():
author = 'Rasheed Elsaleh'
author_email = 'rasheed@rebelliondefense.com'
compatible = ['win32']
tags = ['command']
__version__ = info.version

View File

@@ -131,6 +131,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['dmidecode']
tags = ['command']
__version__ = info.version

View File

@@ -138,6 +138,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['dpkg -l']
tags = ['command']
__version__ = info.version

View File

@@ -98,6 +98,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'aix', 'freebsd']
magic_commands = ['du']
tags = ['command']
__version__ = info.version

View File

@@ -47,6 +47,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'string']
__version__ = info.version

View File

@@ -78,6 +78,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['env', 'printenv']
tags = ['command']
__version__ = info.version

View File

@@ -69,6 +69,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'aix', 'freebsd', 'darwin']
magic_commands = ['file']
tags = ['command']
__version__ = info.version

View File

@@ -93,12 +93,13 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
version = '1.1'
description = '`findmnt` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['findmnt']
tags = ['command']
__version__ = info.version
@@ -140,7 +141,7 @@ def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]:
return proc_data
def _replace(matchobj: re.Match) -> str:
def _replace(matchobj):
if matchobj:
matchlen = len(matchobj.group(1))
return ' ' * matchlen + '/'

View File

@@ -98,6 +98,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'freebsd']
magic_commands = ['finger']
tags = ['command']
__version__ = info.version

View File

@@ -48,6 +48,9 @@ class info():
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
# tags options: generic, standard, file, string, binary, command
tags = ['command']
magic_commands = ['foo']

View File

@@ -58,6 +58,9 @@ class info():
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
# tags options: generic, standard, file, string, binary, command
tags = ['command']
streaming = True

View File

@@ -79,6 +79,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['free']
tags = ['command']
__version__ = info.version

View File

@@ -90,6 +90,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'freebsd']
tags = ['file']
__version__ = info.version

View File

@@ -159,6 +159,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['git log']
tags = ['command']
__version__ = info.version

View File

@@ -93,6 +93,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['command']
streaming = True

View File

@@ -72,6 +72,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['git ls-remote']
tags = ['command']
__version__ = info.version

View File

@@ -126,6 +126,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['gpg --with-colons']
tags = ['command']
__version__ = info.version

View File

@@ -114,6 +114,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'aix', 'freebsd']
tags = ['file']
__version__ = info.version

View File

@@ -82,6 +82,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'aix', 'freebsd']
tags = ['file']
__version__ = info.version

View File

@@ -43,6 +43,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
tags = ['command']
__version__ = info.version

View File

@@ -76,6 +76,7 @@ class info():
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['md5sum', 'md5', 'shasum', 'sha1sum', 'sha224sum',
'sha256sum', 'sha384sum', 'sha512sum']
tags = ['command']
__version__ = info.version

View File

@@ -323,6 +323,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['hciconfig']
tags = ['command']
__version__ = info.version

View File

@@ -69,6 +69,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
details = 'Optimizations by https://github.com/philippeitis'
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
tags = ['command']
__version__ = info.version

View File

@@ -79,6 +79,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['file']
__version__ = info.version

View File

@@ -112,6 +112,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'aix', 'freebsd']
magic_commands = ['id']
tags = ['command']
__version__ = info.version

View File

@@ -219,12 +219,13 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '2.1'
version = '2.2'
description = '`ifconfig` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'aix', 'freebsd', 'darwin']
magic_commands = ['ifconfig']
tags = ['command']
__version__ = info.version
@@ -326,7 +327,7 @@ def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]:
return proc_data
def _bundle_match(pattern_list: List[re.Pattern], string: str) -> Optional[re.Match]:
def _bundle_match(pattern_list, string):
"""Returns a match object if a string matches one of a list of patterns.
If no match is found, returns None"""
for pattern in pattern_list:

View File

@@ -1,10 +1,12 @@
"""jc - JSON Convert `INI` file parser
"""jc - JSON Convert INI file parser
Parses standard `INI` files and files containing simple key/value pairs.
Parses standard INI files.
- Delimiter can be `=` or `:`. Missing values are supported.
- Comment prefix can be `#` or `;`. Comments must be on their own line.
- If duplicate keys are found, only the last value will be used.
- If any section names have the same name as a top-level key, the top-level
key will be overwritten by the section data.
> Note: Values starting and ending with double or single quotation marks
> will have the marks removed. If you would like to keep the quotation
@@ -22,65 +24,82 @@ Usage (module):
Schema:
ini or key/value document converted to a dictionary - see the configparser
INI document converted to a dictionary - see the python configparser
standard library documentation for more details.
{
"key1": string,
"key2": string
"<key1>": string,
"<key2>": string,
"<section1>": {
"<key1>": string,
"<key2>": string
},
"<section2>": {
"<key1>": string,
"<key2>": string
}
}
Examples:
$ cat example.ini
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
foo = fiz
bar = buz
[bitbucket.org]
User = hg
[section1]
fruit = apple
color = blue
[topsecret.server.com]
Port = 50022
ForwardX11 = no
[section2]
fruit = pear
color = green
$ cat example.ini | jc --ini -p
{
"bitbucket.org": {
"ServerAliveInterval": "45",
"Compression": "yes",
"CompressionLevel": "9",
"ForwardX11": "yes",
"User": "hg"
"foo": "fiz",
"bar": "buz",
"section1": {
"fruit": "apple",
"color": "blue"
},
"topsecret.server.com": {
"ServerAliveInterval": "45",
"Compression": "yes",
"CompressionLevel": "9",
"ForwardX11": "no",
"Port": "50022"
"section2": {
"fruit": "pear",
"color": "green"
}
}
"""
import jc.utils
import configparser
import uuid
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.8'
version = '2.0'
description = 'INI file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using configparser from the standard library'
details = 'Using configparser from the python standard library'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'file', 'string']
__version__ = info.version
def _remove_quotes(value):
if value is None:
value = ''
elif value.startswith('"') and value.endswith('"'):
value = value[1:-1]
elif value.startswith("'") and value.endswith("'"):
value = value[1:-1]
return value
def _process(proc_data):
"""
Final processing to conform to the schema.
@@ -91,32 +110,16 @@ def _process(proc_data):
Returns:
Dictionary representing an ini or simple key/value pair document.
Dictionary representing the INI file.
"""
# remove quotation marks from beginning and end of values
for heading in proc_data:
# standard ini files with headers
if isinstance(proc_data[heading], dict):
for key, value in proc_data[heading].items():
if value is not None and value.startswith('"') and value.endswith('"'):
proc_data[heading][key] = value.lstrip('"').rstrip('"')
for k, v in proc_data.items():
if isinstance(v, dict):
for key, value in v.items():
v[key] = _remove_quotes(value)
continue
elif value is not None and value.startswith("'") and value.endswith("'"):
proc_data[heading][key] = value.lstrip("'").rstrip("'")
elif value is None:
proc_data[heading][key] = ''
# simple key/value files with no headers
else:
if proc_data[heading] is not None and proc_data[heading].startswith('"') and proc_data[heading].endswith('"'):
proc_data[heading] = proc_data[heading].lstrip('"').rstrip('"')
elif proc_data[heading] is not None and proc_data[heading].startswith("'") and proc_data[heading].endswith("'"):
proc_data[heading] = proc_data[heading].lstrip("'").rstrip("'")
elif proc_data[heading] is None:
proc_data[heading] = ''
proc_data[k] = _remove_quotes(v)
return proc_data
@@ -133,7 +136,7 @@ def parse(data, raw=False, quiet=False):
Returns:
Dictionary representing the ini file
Dictionary representing the INI file.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
@@ -142,25 +145,36 @@ def parse(data, raw=False, quiet=False):
if jc.utils.has_data(data):
ini = configparser.ConfigParser(allow_no_value=True,
interpolation=None,
strict=False)
ini_parser = configparser.ConfigParser(
allow_no_value=True,
interpolation=None,
default_section=None,
strict=False
)
# don't convert keys to lower-case:
ini.optionxform = lambda option: option
ini_parser.optionxform = lambda option: option
try:
ini.read_string(data)
raw_output = {s: dict(ini.items(s)) for s in ini.sections()}
ini_parser.read_string(data)
raw_output = {s: dict(ini_parser.items(s)) for s in ini_parser.sections()}
except configparser.MissingSectionHeaderError:
data = '[data]\n' + data
ini.read_string(data)
output_dict = {s: dict(ini.items(s)) for s in ini.sections()}
for key, value in output_dict['data'].items():
raw_output[key] = value
# find a top-level section name that will not collide with any existing ones
while True:
my_uuid = str(uuid.uuid4())
if my_uuid not in data:
break
data = f'[{my_uuid}]\n' + data
ini_parser.read_string(data)
temp_dict = {s: dict(ini_parser.items(s)) for s in ini_parser.sections()}
# move items under fake top-level sections to the root
raw_output = temp_dict.pop(my_uuid)
# get the rest of the sections
raw_output.update(temp_dict)
return raw_output if raw else _process(raw_output)
if raw:
return raw_output
else:
return _process(raw_output)

225
jc/parsers/ini_dup.py Normal file
View File

@@ -0,0 +1,225 @@
"""jc - JSON Convert INI with duplicate key file parser
Parses standard INI files and preserves duplicate values. All values are
contained in lists/arrays.
- Delimiter can be `=` or `:`. Missing values are supported.
- Comment prefix can be `#` or `;`. Comments must be on their own line.
- If any section names have the same name as a top-level key, the top-level
key will be overwritten by the section data.
- If multi-line values are used, each line will be a separate item in the
value list. Blank lines in multi-line values are not supported.
> Note: Values starting and ending with double or single quotation marks
> will have the marks removed. If you would like to keep the quotation
> marks, use the `-r` command-line argument or the `raw=True` argument in
> `parse()`.
Usage (cli):
$ cat foo.ini | jc --ini
Usage (module):
import jc
result = jc.parse('ini', ini_file_output)
Schema:
INI document converted to a dictionary - see the python configparser
standard library documentation for more details.
{
"<key1>": [
string
],
"<key2>": [
string
],
"<section1>": {
"<key1>": [
string
],
"<key2>": [
string
]
}
}
Examples:
$ cat example.ini
foo = fiz
bar = buz
[section1]
fruit = apple
color = blue
color = red
[section2]
fruit = pear
fruit = peach
color = green
$ cat example.ini | jc --ini -p
{
"foo": [
"fiz"
],
"bar": [
"buz"
],
"section1": {
"fruit": [
"apple"
],
"color": [
"blue",
"red"
]
},
"section2": {
"fruit": [
"pear",
"peach"
],
"color": [
"green"
]
}
}
"""
import jc.utils
import configparser
import uuid
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = 'INI with duplicate key file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using configparser from the python standard library'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'file', 'string']
__version__ = info.version
class MultiDict(dict):
# https://stackoverflow.com/a/38286559/12303989
def __setitem__(self, key, value):
if key in self:
if isinstance(value, list):
self[key].extend(value)
elif isinstance(value, str):
if len(self[key])>1:
return
else:
super().__setitem__(key, value)
def _remove_quotes(value):
if value is None:
value = ''
elif value.startswith('"') and value.endswith('"'):
value = value[1:-1]
elif value.startswith("'") and value.endswith("'"):
value = value[1:-1]
return value
def _process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (Dictionary) raw structured data to process
Returns:
Dictionary representing the INI file.
"""
# remove quotation marks from beginning and end of values
for k, v in proc_data.items():
if isinstance(v, dict):
for key, value in v.items():
if isinstance(value, list):
v[key] = [_remove_quotes(x) for x in value]
else:
v[key] = _remove_quotes(value)
continue
elif isinstance(v, list):
proc_data[k] = [_remove_quotes(x) for x in v]
else:
proc_data[k] = _remove_quotes(v)
return proc_data
def parse(data, raw=False, quiet=False):
"""
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 representing the INI file.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output = {}
if jc.utils.has_data(data):
ini_parser = configparser.ConfigParser(
dict_type = MultiDict,
allow_no_value=True,
interpolation=None,
default_section=None,
empty_lines_in_values=False,
strict=False
)
# don't convert keys to lower-case:
ini_parser.optionxform = lambda option: option
try:
ini_parser.read_string(data)
raw_output = {s: dict(ini_parser.items(s)) for s in ini_parser.sections()}
except configparser.MissingSectionHeaderError:
# find a top-level section name that will not collide with any existing ones
while True:
my_uuid = str(uuid.uuid4())
if my_uuid not in data:
break
data = f'[{my_uuid}]\n' + data
ini_parser.read_string(data)
temp_dict = {s: dict(ini_parser.items(s)) for s in ini_parser.sections()}
# move items under fake top-level sections to the root
raw_output = temp_dict.pop(my_uuid)
# get the rest of the sections
raw_output.update(temp_dict)
return raw_output if raw else _process(raw_output)

View File

@@ -166,6 +166,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['iostat']
tags = ['command']
__version__ = info.version

View File

@@ -113,6 +113,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
tags = ['command']
streaming = True

View File

@@ -538,6 +538,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'string']
__version__ = info.version

View File

@@ -169,6 +169,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['iptables']
tags = ['command']
__version__ = info.version

View File

@@ -17,6 +17,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
details = 'Deprecated - please use datetime-iso'
compatible = ['linux', 'aix', 'freebsd', 'darwin', 'win32', 'cygwin']
tags = ['standard', 'string']
deprecated = True

View File

@@ -129,6 +129,7 @@ class info():
details = 'Enhancements by Philipp Schmitt (https://pschmitt.dev/)'
compatible = ['linux']
magic_commands = ['iw dev']
tags = ['command']
__version__ = info.version

202
jc/parsers/iwconfig.py Normal file
View File

@@ -0,0 +1,202 @@
"""jc - JSON Convert `iwconfig` command output parser
No `iwconfig` options are supported.
Usage (cli):
$ iwconfig | jc --iwconfig
or
$ jc iwconfig
Usage (module):
import jc
result = jc.parse('iwconfig', iwconfig_command_output)
Schema:
[
{
"name": string,
"protocol": string,
"essid": string,
"mode": string,
"frequency": float,
"frequency_unit": string,
"access_point": string,
"bit_rate": float,
"bit_rate_unit": string,
"tx_power": integer,
"tx_power_unit": string,
"retry_short_limit": integer,
"rts_threshold": boolean,
"fragment_threshold": boolean,
"power_management": boolean,
"link_quality": string,
"signal_level": integer,
"signal_level_unit": string,
"rx_invalid_nwid": integer,
"rx_invalid_crypt": integer,
"rx_invalid_frag": integer,
"tx_excessive_retries": integer,
"invalid_misc": integer,
"missed_beacon": integer
}
]
Examples:
$ iwconfig | jc --iwconfig -p
[
{
"name": "wlp5s0",
"protocol": "IEEE 802.11",
"essid": "BLABLABLA",
"mode": "Managed",
"frequency": 5.18,
"frequency_unit": "GHz",
"access_point": "E6:64:DA:16:51:BF",
"bit_rate": 6.0,
"bit_rate_unit": "Mb/s",
"tx_power": 30,
"tx_power_unit": "dBm",
"retry_short_limit": 7,
"rts_threshold": false,
"fragment_threshold": false,
"power_management": true,
"link_quality": "61/70",
"signal_level": -49,
"signal_level_unit": "dBm",
"rx_invalid_nwid": 0,
"rx_invalid_crypt": 0,
"rx_invalid_frag": 0,
"tx_excessive_retries": 0,
"invalid_misc": 2095,
"missed_beacon": 0
}
]
"""
import re
from typing import List, Dict
from jc.jc_types import JSONDictType
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`iwconfig` command parser'
author = 'Thomas Vincent'
author_email = 'vrince@gmail.com'
compatible = ['linux']
magic_commands = ['iwconfig']
tags = ['command']
__version__ = info.version
def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
List of Dictionaries. Structured to conform to the schema.
"""
int_list = [
'signal_level', 'rx_invalid_nwid', 'rx_invalid_crypt', 'rx_invalid_frag',
'tx_excessive_retries', 'invalid_misc', 'missed_beacon', 'tx_power', 'retry_short_limit'
]
float_list = ['frequency', 'bit_rate']
bool_list = ['rts_threshold', 'fragment_threshold', 'power_management']
proc_data = [ { key: int(value) if key in int_list else value for key, value in proc_data_item.items() } for proc_data_item in proc_data ]
proc_data = [ { key: float(value) if key in float_list else value for key, value in proc_data_item.items() } for proc_data_item in proc_data ]
proc_data = [ { key: value == 'on' if key in bool_list else value for key, value in proc_data_item .items() } for proc_data_item in proc_data ]
return proc_data
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> List[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:
List of Dictionaries. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: List[Dict] = []
re_interface = re.compile(r'^(?P<name>[a-zA-Z0-9:._-]+)\s+(?P<protocol>([a-zA-Z0-9]+\s)*[a-zA-Z0-9.]+)\s+ESSID:\"(?P<essid>[a-zA-Z0-9:._\s]+)\"')
re_mode = re.compile(r'Mode:(?P<mode>\w+)')
re_frequency = re.compile(r'Frequency:(?P<frequency>[0-9.]+)\s(?P<frequency_unit>\w+)')
re_access_point = re.compile(r'Access Point:\s*(?P<access_point>[0-9A-F:]+)')
re_bit_rate = re.compile(r'Bit Rate=(?P<bit_rate>[0-9.]+)\s(?P<bit_rate_unit>[\w\/]+)')
re_tx_power= re.compile(r'Tx-Power=(?P<tx_power>[-0-9]+)\s(?P<tx_power_unit>[\w]+)')
re_retry_short_limit = re.compile(r'Retry short limit:(?P<retry_short_limit>[0-9\/]+)')
re_rts_threshold = re.compile(r'RTS thr:(?P<rts_threshold>(off|on))')
re_fragment_threshold = re.compile(r'Fragment thr:(?P<fragment_threshold>(off|on))')
re_power_management = re.compile(r'Power Management:(?P<power_management>(off|on))')
re_link_quality = re.compile(r'Link Quality=(?P<link_quality>[0-9\/]+)')
re_signal_level = re.compile(r'Signal level=(?P<signal_level>[-0-9]+)\s(?P<signal_level_unit>[\w]+)')
re_rx_invalid_nwid = re.compile(r'Rx invalid nwid:(?P<rx_invalid_nwid>[-0-9]+)')
re_rx_invalid_crypt = re.compile(r'Rx invalid crypt:(?P<rx_invalid_crypt>[-0-9]+)')
re_rx_invalid_frag = re.compile(r'Rx invalid frag:(?P<rx_invalid_frag>[-0-9]+)')
re_tx_excessive_retries = re.compile(r'Tx excessive retries:(?P<tx_excessive_retries>[-0-9]+)')
re_invalid_misc = re.compile(r'Invalid misc:(?P<invalid_misc>[0-9]+)')
re_missed_beacon = re.compile(r'Missed beacon:(?P<missed_beacon>[0-9]+)')
re_all = [
re_mode, re_frequency, re_access_point, re_bit_rate, re_tx_power,
re_retry_short_limit, re_rts_threshold, re_fragment_threshold, re_power_management,
re_link_quality, re_signal_level, re_rx_invalid_nwid, re_rx_invalid_crypt,
re_rx_invalid_frag, re_tx_excessive_retries, re_invalid_misc, re_missed_beacon
]
interface_item = None
if jc.utils.has_data(data):
for line in filter(None, data.splitlines()):
# Find new interface lines
interface_match = re.search(re_interface, line)
if interface_match:
if interface_item is not None:
raw_output.append(interface_item)
interface_item = dict()
interface_item.update(interface_match.groupdict())
continue
# we do not have any interface yet continue to search for it --> next line
if interface_item is None:
continue
# Filling interface with whatever we can find
for re_entry in re_all:
match = re.search(re_entry, line)
if match:
interface_item.update(match.groupdict())
if interface_item is not None:
raw_output.append(interface_item)
return raw_output if raw else _process(raw_output)

View File

@@ -83,6 +83,7 @@ class info():
author = 'Matt J'
author_email = 'https://github.com/listuser'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['file']
__version__ = info.version

View File

@@ -99,6 +99,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['jobs']
tags = ['command']
__version__ = info.version

View File

@@ -56,6 +56,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['standard', 'string']
__version__ = info.version

View File

@@ -1,4 +1,4 @@
"""jc - JSON Convert `Key/Value` file parser
"""jc - JSON Convert `Key/Value` file and string parser
Supports files containing simple key/value pairs.
@@ -21,8 +21,8 @@ Usage (module):
Schema:
key/value document converted to a dictionary - see the configparser standard
library documentation for more details.
Key/Value document converted to a dictionary - see the python configparser
standard library documentation for more details.
{
"key1": string,
@@ -36,6 +36,7 @@ Examples:
name = John Doe
address=555 California Drive
age: 34
; comments can include # or ;
# delimiter can be = or :
# quoted values have quotation marks stripped by default
@@ -50,27 +51,54 @@ Examples:
"occupation": "Engineer"
}
"""
import jc.utils
import configparser
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.2'
description = 'Key/Value file parser'
version = '2.0'
description = 'Key/Value file and string parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'This is a wrapper for the INI parser'
details = 'Using configparser from the python standard library'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['generic', 'file', 'string']
__version__ = info.version
def _process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (Dictionary) raw structured data to process
Returns:
Dictionary representing a Key/Value pair document.
"""
# remove quotation marks from beginning and end of values
for key in proc_data:
if proc_data[key] is None:
proc_data[key] = ''
elif proc_data[key].startswith('"') and proc_data[key].endswith('"'):
proc_data[key] = proc_data[key][1:-1]
elif proc_data[key].startswith("'") and proc_data[key].endswith("'"):
proc_data[key] = proc_data[key][1:-1]
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Note: this is just a wrapper for jc.parsers.ini
Parameters:
data: (string) text data to parse
@@ -79,7 +107,30 @@ def parse(data, raw=False, quiet=False):
Returns:
Dictionary representing the key/value file
Dictionary representing a Key/Value pair document.
"""
import jc.parsers.ini
return jc.parsers.ini.parse(data, raw=raw, quiet=quiet)
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output = {}
if jc.utils.has_data(data):
kv_parser = configparser.ConfigParser(
allow_no_value=True,
interpolation=None,
default_section=None,
strict=False
)
# don't convert keys to lower-case:
kv_parser.optionxform = lambda option: option
data = '[data]\n' + data
kv_parser.read_string(data)
output_dict = {s: dict(kv_parser.items(s)) for s in kv_parser.sections()}
for key, value in output_dict['data'].items():
raw_output[key] = value
return raw_output if raw else _process(raw_output)

View File

@@ -113,6 +113,7 @@ class info():
details = 'Enhancements by https://github.com/zerolagtime'
compatible = ['linux', 'darwin', 'aix', 'freebsd']
magic_commands = ['last', 'lastb']
tags = ['command']
__version__ = info.version

View File

@@ -124,6 +124,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['ls', 'vdir']
tags = ['command']
__version__ = info.version

View File

@@ -82,6 +82,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
tags = ['command']
streaming = True

View File

@@ -281,6 +281,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['lsblk']
tags = ['command']
__version__ = info.version

View File

@@ -132,6 +132,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['lsmod']
tags = ['command']
__version__ = info.version

View File

@@ -126,6 +126,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'aix', 'freebsd']
magic_commands = ['lsof']
tags = ['command']
__version__ = info.version

View File

@@ -129,6 +129,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['lspci']
tags = ['command']
__version__ = info.version

View File

@@ -97,6 +97,28 @@ Schema:
]
}
},
"videocontrol_descriptors": [
{
"<item>": {
"value": string,
"description": string,
"attributes": [
string
]
}
}
],
"videostreaming_descriptors": [
{
"<item>": {
"value": string,
"description": string,
"attributes": [
string
]
}
}
],
"endpoint_descriptors": [
{
"<item>": {
@@ -269,12 +291,13 @@ from jc.exceptions import ParseError
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.2'
version = '1.3'
description = '`lsusb` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['lsusb']
tags = ['command']
__version__ = info.version
@@ -306,6 +329,145 @@ class _NestedDict(dict):
return self.setdefault(key, _NestedDict())
class _root_obj:
def __init__(self, name):
self.name = name
self.list = []
def _entries_for_this_bus_exist(self, bus_idx):
"""Returns true if there are object entries for the corresponding bus index"""
for item in self.list:
keyname = tuple(item.keys())[0]
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx:
return True
return False
def _update_output(self, bus_idx, output_line):
"""modifies output_line dictionary for the corresponding bus index.
output_line is the self.output_line attribute from the _lsusb object."""
for item in self.list:
keyname = tuple(item.keys())[0]
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx:
# is this a top level value or an attribute?
if item[keyname]['_state']['attribute_value']:
last_item = item[keyname]['_state']['last_item']
if 'attributes' not in output_line[f'{self.name}'][last_item]:
output_line[f'{self.name}'][last_item]['attributes'] = []
this_attribute = f'{keyname} {item[keyname].get("value", "")} {item[keyname].get("description", "")}'.strip()
output_line[f'{self.name}'][last_item]['attributes'].append(this_attribute)
continue
output_line[f'{self.name}'].update(item)
del output_line[f'{self.name}'][keyname]['_state']
class _descriptor_obj:
def __init__(self, name):
self.name = name
self.list = []
def _entries_for_this_bus_and_interface_idx_exist(self, bus_idx, iface_idx):
"""Returns true if there are object entries for the corresponding bus index
and interface index"""
for item in self.list:
keyname = tuple(item.keys())[0]
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
return True
return False
def _update_output(self, bus_idx, iface_idx, output_line):
"""modifies output_line dictionary for the corresponding bus index and
interface index. output_line is the i_desc_obj object."""
for item in self.list:
keyname = tuple(item.keys())[0]
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
# is this a top level value or an attribute?
if item[keyname]['_state']['attribute_value']:
last_item = item[keyname]['_state']['last_item']
if 'attributes' not in output_line[f'{self.name}'][last_item]:
output_line[f'{self.name}'][last_item]['attributes'] = []
this_attribute = f'{keyname} {item[keyname].get("value", "")} {item[keyname].get("description", "")}'.strip()
output_line[f'{self.name}'][last_item]['attributes'].append(this_attribute)
continue
output_line[f'{self.name}'].update(item)
del output_line[f'{self.name}'][keyname]['_state']
class _descriptor_list:
def __init__(self, name):
self.name = name
self.list = []
def _entries_for_this_bus_and_interface_idx_exist(self, bus_idx, iface_idx):
"""Returns true if there are object entries for the corresponding bus index
and interface index"""
for item in self.list:
keyname = tuple(item.keys())[0]
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
return True
return False
def _get_objects_list(self, bus_idx, iface_idx):
"""Returns a list of descriptor object dictionaries for the corresponding
bus index and interface index"""
object_collection = []
# find max number of items in this object that match the bus_idx and iface_idx
num_of_items = -1
for item in self.list:
keyname = tuple(item.keys())[0]
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
num_of_items = item[keyname]['_state'][f'{self.name}_idx']
# create and return the collection of objects that match the bus_idx and iface_idx
if num_of_items > -1:
for obj_idx in range(num_of_items + 1):
this_object = {}
for item in self.list:
keyname = tuple(item.keys())[0]
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx \
and item[keyname]['_state'][f'{self.name}_idx'] == obj_idx:
# is this a top level value or an attribute?
if item[keyname]['_state']['attribute_value']:
last_item = item[keyname]['_state']['last_item']
if 'attributes' not in this_object[last_item]:
this_object[last_item]['attributes'] = []
this_attribute = f'{keyname} {item[keyname].get("value", "")} {item[keyname].get("description", "")}'.strip()
this_object[last_item]['attributes'].append(this_attribute)
continue
this_object.update(item)
del item[keyname]['_state']
object_collection.append(this_object)
return object_collection
class _LsUsb():
def __init__(self):
self.raw_output = []
@@ -313,27 +475,37 @@ class _LsUsb():
self.section = ''
self.old_section = ''
# section_header is formatted with the correct spacing to be used with
# jc.parsers.universal.sparse_table_parse(). Pad end of string to be at least len of 25
# this value changes for different sections (e.g. videocontrol & videostreaming)
self.normal_section_header = 'key val description'
self.larger_section_header = 'key val description'
self.bus_idx = -1
self.interface_descriptor_idx = -1
self.endpoint_descriptor_idx = -1
self.videocontrol_interface_descriptor_idx = -1
self.videostreaming_interface_descriptor_idx = -1
self.last_item = ''
self.last_indent = 0
self.attribute_value = False
self.bus_list = []
self.device_descriptor_list = []
self.configuration_descriptor_list = []
self.interface_association_list = []
self.device_descriptor = _root_obj('device_descriptor')
self.configuration_descriptor = _root_obj('configuration_descriptor')
self.interface_association = _root_obj('interface_association')
self.interface_descriptor_list = []
self.interface_descriptor_attribute_list = []
self.cdc_header_list = []
self.cdc_call_management_list = []
self.cdc_acm_list = []
self.cdc_union_list = []
self.endpoint_descriptor_list = []
self.hid_device_descriptor_list = []
self.report_descriptors_list = []
self.hub_descriptor_list = []
self.cdc_header = _descriptor_obj('cdc_header')
self.cdc_call_management = _descriptor_obj('cdc_call_management')
self.cdc_acm = _descriptor_obj('cdc_acm')
self.cdc_union = _descriptor_obj('cdc_union')
self.endpoint_descriptors = _descriptor_list('endpoint_descriptor')
self.videocontrol_interface_descriptors = _descriptor_list('videocontrol_interface_descriptor')
self.videostreaming_interface_descriptors = _descriptor_list('videostreaming_interface_descriptor')
self.hid_device_descriptor = _descriptor_obj('hid_device_descriptor')
# self.report_descriptors_list = [] # not implemented
self.hub_descriptor = _root_obj('hub_descriptor')
self.hub_port_status_list = []
self.device_qualifier_list = []
self.device_status_list = []
@@ -354,14 +526,21 @@ class _LsUsb():
# determine whether this is a top-level value item or lower-level attribute
if indent > self.last_indent and self.old_section == self.section:
self.attribute_value = True
elif indent == self.last_indent and self.attribute_value and self.old_section == self.section:
elif indent == self.last_indent and self.attribute_value \
and self.old_section == self.section:
self.attribute_value = True
else:
self.attribute_value = False
# section_header is formatted with the correct spacing to be used with
# jc.parsers.universal.sparse_table_parse(). Pad end of string to be at least len of 25
section_header = 'key val description'
section_header = self.normal_section_header
if self.section == 'videocontrol_interface_descriptor' \
or self.section == 'videostreaming_interface_descriptor':
section_header = self.larger_section_header
temp_obj = [section_header, line.strip() + (' ' * 25)]
temp_obj = sparse_table_parse(temp_obj)
@@ -376,7 +555,9 @@ class _LsUsb():
'last_item': self.last_item,
'bus_idx': self.bus_idx,
'interface_descriptor_idx': self.interface_descriptor_idx,
'endpoint_descriptor_idx': self.endpoint_descriptor_idx
'endpoint_descriptor_idx': self.endpoint_descriptor_idx,
'videocontrol_interface_descriptor_idx': self.videocontrol_interface_descriptor_idx,
'videostreaming_interface_descriptor_idx': self.videostreaming_interface_descriptor_idx
}
}
}
@@ -428,12 +609,15 @@ class _LsUsb():
self.attribute_value = False
return True
# bus information is on the same line so need to extract data immediately and set indexes
# bus information is on the same line so need to extract data
# immediately and set indexes
if line.startswith('Bus '):
self.section = 'bus'
self.bus_idx += 1
self.interface_descriptor_idx = -1
self.endpoint_descriptor_idx = -1
self.videocontrol_interface_descriptor_idx = -1
self.videostreaming_interface_descriptor_idx = -1
self.attribute_value = False
line_split = line.strip().split(maxsplit=6)
self.bus_list.append(
@@ -441,7 +625,8 @@ class _LsUsb():
'bus': line_split[1],
'device': line_split[3][:-1],
'id': line_split[5],
'description': (line_split[6:7] or [None])[0], # way to get a list item or None
# way to get a list item or None
'description': (line_split[6:7] or [None])[0],
'_state': {
'bus_idx': self.bus_idx
}
@@ -449,22 +634,36 @@ class _LsUsb():
)
return True
# This section is a list, so need to update indexes
# These sections are lists, so need to update indexes
if line.startswith(' Interface Descriptor:'):
self.section = 'interface_descriptor'
self.interface_descriptor_idx += 1
self.endpoint_descriptor_idx = -1
self.videocontrol_interface_descriptor_idx = -1
self.videostreaming_interface_descriptor_idx = -1
self.attribute_value = False
return True
# This section is a list, so need to update the index
if line.startswith(' Endpoint Descriptor:'):
self.section = 'endpoint_descriptor'
self.endpoint_descriptor_idx += 1
self.attribute_value = False
return True
# some device status information is displayed on the initial line so need to extract immediately
if line.startswith(' VideoControl Interface Descriptor:'):
self.section = 'videocontrol_interface_descriptor'
self.videocontrol_interface_descriptor_idx += 1
self.attribute_value = False
return True
if line.startswith(' VideoStreaming Interface Descriptor:'):
self.section = 'videostreaming_interface_descriptor'
self.videostreaming_interface_descriptor_idx += 1
self.attribute_value = False
return True
# some device status information is displayed on the initial line so
# need to extract immediately
if line.startswith('Device Status:'):
self.section = 'device_status'
self.attribute_value = False
@@ -506,18 +705,20 @@ class _LsUsb():
def _populate_lists(self, line):
section_list_map = {
'device_descriptor': self.device_descriptor_list,
'configuration_descriptor': self.configuration_descriptor_list,
'interface_association': self.interface_association_list,
'device_descriptor': self.device_descriptor.list,
'configuration_descriptor': self.configuration_descriptor.list,
'interface_association': self.interface_association.list,
'interface_descriptor': self.interface_descriptor_list,
'cdc_header': self.cdc_header_list,
'cdc_call_management': self.cdc_call_management_list,
'cdc_acm': self.cdc_acm_list,
'cdc_union': self.cdc_union_list,
'hid_device_descriptor': self.hid_device_descriptor_list,
'report_descriptors': self.report_descriptors_list,
'endpoint_descriptor': self.endpoint_descriptor_list,
'hub_descriptor': self.hub_descriptor_list,
'cdc_header': self.cdc_header.list,
'cdc_call_management': self.cdc_call_management.list,
'cdc_acm': self.cdc_acm.list,
'cdc_union': self.cdc_union.list,
'hid_device_descriptor': self.hid_device_descriptor.list,
# 'report_descriptors': self.report_descriptors_list, # not implemented
'videocontrol_interface_descriptor': self.videocontrol_interface_descriptors.list,
'videostreaming_interface_descriptor': self.videostreaming_interface_descriptors.list,
'endpoint_descriptor': self.endpoint_descriptors.list,
'hub_descriptor': self.hub_descriptor.list,
'device_qualifier': self.device_qualifier_list
}
@@ -527,7 +728,9 @@ class _LsUsb():
return True
# special handling of these sections
if line.startswith(' ') and self.section == 'hub_port_status':
if line.startswith(' ') and not line.startswith(' ') \
and self.section == 'hub_port_status':
self.hub_port_status_list.append(self._add_hub_port_status_attributes(line))
return True
@@ -546,6 +749,10 @@ class _LsUsb():
['device_descriptor']['configuration_descriptor']['interface_association'] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'] = []
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['videocontrol_interface_descriptors'] = []
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['videocontrol_interface_descriptors'][0] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['videostreaming_interface_descriptors'] = []
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['videostreaming_interface_descriptors'][0] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_header'] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_call_management'] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_acm'] = {}
@@ -567,81 +774,56 @@ class _LsUsb():
del item['_state']
self.output_line.update(item)
for dd in self.device_descriptor_list:
keyname = tuple(dd.keys())[0]
if '_state' in dd[keyname] and dd[keyname]['_state']['bus_idx'] == idx:
# add initial root-level keys
if self.device_descriptor._entries_for_this_bus_exist(idx):
self.device_descriptor._update_output(idx, self.output_line)
# is this a top level value or an attribute?
if dd[keyname]['_state']['attribute_value']:
last_item = dd[keyname]['_state']['last_item']
if 'attributes' not in self.output_line['device_descriptor'][last_item]:
self.output_line['device_descriptor'][last_item]['attributes'] = []
if self.configuration_descriptor._entries_for_this_bus_exist(idx):
self.configuration_descriptor._update_output(
idx, self.output_line['device_descriptor']
)
this_attribute = f'{keyname} {dd[keyname].get("value", "")} {dd[keyname].get("description", "")}'.strip()
self.output_line['device_descriptor'][last_item]['attributes'].append(this_attribute)
continue
if self.interface_association._entries_for_this_bus_exist(idx):
self.interface_association._update_output(
idx, self.output_line['device_descriptor']['configuration_descriptor']
)
self.output_line['device_descriptor'].update(dd)
del self.output_line['device_descriptor'][keyname]['_state']
for cd in self.configuration_descriptor_list:
keyname = tuple(cd.keys())[0]
if '_state' in cd[keyname] and cd[keyname]['_state']['bus_idx'] == idx:
# is this a top level value or an attribute?
if cd[keyname]['_state']['attribute_value']:
last_item = cd[keyname]['_state']['last_item']
if 'attributes' not in self.output_line['device_descriptor']['configuration_descriptor'][last_item]:
self.output_line['device_descriptor']['configuration_descriptor'][last_item]['attributes'] = []
this_attribute = f'{keyname} {cd[keyname].get("value", "")} {cd[keyname].get("description", "")}'.strip()
self.output_line['device_descriptor']['configuration_descriptor'][last_item]['attributes'].append(this_attribute)
continue
self.output_line['device_descriptor']['configuration_descriptor'].update(cd)
del self.output_line['device_descriptor']['configuration_descriptor'][keyname]['_state']
for ia in self.interface_association_list:
keyname = tuple(ia.keys())[0]
if '_state' in ia[keyname] and ia[keyname]['_state']['bus_idx'] == idx:
# is this a top level value or an attribute?
if ia[keyname]['_state']['attribute_value']:
last_item = ia[keyname]['_state']['last_item']
if 'attributes' not in self.output_line['device_descriptor']['configuration_descriptor']['interface_association'][last_item]:
self.output_line['device_descriptor']['configuration_descriptor']['interface_association'][last_item]['attributes'] = []
this_attribute = f'{keyname} {ia[keyname].get("value", "")} {ia[keyname].get("description", "")}'.strip()
self.output_line['device_descriptor']['configuration_descriptor']['interface_association'][last_item]['attributes'].append(this_attribute)
continue
self.output_line['device_descriptor']['configuration_descriptor']['interface_association'].update(ia)
del self.output_line['device_descriptor']['configuration_descriptor']['interface_association'][keyname]['_state']
# add interface_descriptor key if it doesn't exist and there are entries for this bus
# add interface_descriptor key if it doesn't exist and there
# are entries for this bus
for iface_attrs in self.interface_descriptor_list:
keyname = tuple(iface_attrs.keys())[0]
if '_state' in iface_attrs[keyname] and iface_attrs[keyname]['_state']['bus_idx'] == idx:
if '_state' in iface_attrs[keyname] \
and iface_attrs[keyname]['_state']['bus_idx'] == idx:
self.output_line['device_descriptor']['configuration_descriptor']['interface_descriptors'] = []
# find max index for this bus idx, then iterate over that range
i_desc_iters = -1
for iface_attrs in self.interface_descriptor_list:
keyname = tuple(iface_attrs.keys())[0]
if '_state' in iface_attrs[keyname] and iface_attrs[keyname]['_state']['bus_idx'] == idx:
if '_state' in iface_attrs[keyname] \
and iface_attrs[keyname]['_state']['bus_idx'] == idx:
i_desc_iters = iface_attrs[keyname]['_state']['interface_descriptor_idx']
# create the interface descriptor object
if i_desc_iters > -1:
for iface_idx in range(i_desc_iters + 1):
i_desc_obj = _NestedDict()
for iface_attrs in self.interface_descriptor_list:
keyname = tuple(iface_attrs.keys())[0]
if '_state' in iface_attrs[keyname] and iface_attrs[keyname]['_state']['bus_idx'] == idx and iface_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
if '_state' in iface_attrs[keyname] \
and iface_attrs[keyname]['_state']['bus_idx'] == idx \
and iface_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
# is this a top level value or an attribute?
if iface_attrs[keyname]['_state']['attribute_value']:
last_item = iface_attrs[keyname]['_state']['last_item']
if 'attributes' not in i_desc_obj[last_item]:
i_desc_obj[last_item]['attributes'] = []
@@ -652,168 +834,70 @@ class _LsUsb():
del iface_attrs[keyname]['_state']
i_desc_obj.update(iface_attrs)
# add other nodes to the object (cdc_header, endpoint descriptors, etc.)
for ch in self.cdc_header_list:
keyname = tuple(ch.keys())[0]
if '_state' in ch[keyname] and ch[keyname]['_state']['bus_idx'] == idx and ch[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
# add the rest of the interface descriptor keys to the object
if self.cdc_header._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
self.cdc_header._update_output(idx, iface_idx, i_desc_obj)
# is this a top level value or an attribute?
if ch[keyname]['_state']['attribute_value']:
last_item = ch[keyname]['_state']['last_item']
if 'attributes' not in i_desc_obj['cdc_header'][last_item]:
i_desc_obj['cdc_header'][last_item]['attributes'] = []
if self.cdc_call_management._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
self.cdc_call_management._update_output(idx, iface_idx, i_desc_obj)
this_attribute = f'{keyname} {ch[keyname].get("value", "")} {ch[keyname].get("description", "")}'.strip()
i_desc_obj['cdc_header'][last_item]['attributes'].append(this_attribute)
continue
if self.cdc_acm._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
self.cdc_acm._update_output(idx, iface_idx, i_desc_obj)
i_desc_obj['cdc_header'].update(ch)
del i_desc_obj['cdc_header'][keyname]['_state']
if self.cdc_union._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
self.cdc_union._update_output(idx, iface_idx, i_desc_obj)
for ccm in self.cdc_call_management_list:
keyname = tuple(ccm.keys())[0]
if '_state' in ccm[keyname] and ccm[keyname]['_state']['bus_idx'] == idx and ccm[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
if self.hid_device_descriptor._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
self.hid_device_descriptor._update_output(idx, iface_idx, i_desc_obj)
# is this a top level value or an attribute?
if ccm[keyname]['_state']['attribute_value']:
last_item = ccm[keyname]['_state']['last_item']
if 'attributes' not in i_desc_obj['cdc_call_management'][last_item]:
i_desc_obj['cdc_call_management'][last_item]['attributes'] = []
# Not Implemented: Report Descriptors (need more samples)
# for rd in self.report_descriptors_list:
# keyname = tuple(rd.keys())[0]
# if '_state' in rd[keyname] and rd[keyname]['_state']['bus_idx'] == idx and rd[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
# i_desc_obj['hid_device_descriptor']['report_descriptors'].update(rd)
# del i_desc_obj['hid_device_descriptor']['report_descriptors'][keyname]['_state']
this_attribute = f'{keyname} {ccm[keyname].get("value", "")} {ccm[keyname].get("description", "")}'.strip()
i_desc_obj['cdc_call_management'][last_item]['attributes'].append(this_attribute)
continue
if self.videocontrol_interface_descriptors._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
i_desc_obj['videocontrol_interface_descriptors'] = []
i_desc_obj['videocontrol_interface_descriptors'].extend(
self.videocontrol_interface_descriptors._get_objects_list(idx, iface_idx)
)
i_desc_obj['cdc_call_management'].update(ccm)
del i_desc_obj['cdc_call_management'][keyname]['_state']
if self.videostreaming_interface_descriptors._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
i_desc_obj['videostreaming_interface_descriptors'] = []
i_desc_obj['videostreaming_interface_descriptors'].extend(
self.videostreaming_interface_descriptors._get_objects_list(idx, iface_idx)
)
for ca in self.cdc_acm_list:
keyname = tuple(ca.keys())[0]
if '_state' in ca[keyname] and ca[keyname]['_state']['bus_idx'] == idx and ca[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
# is this a top level value or an attribute?
if ca[keyname]['_state']['attribute_value']:
last_item = ca[keyname]['_state']['last_item']
if 'attributes' not in i_desc_obj['cdc_acm'][last_item]:
i_desc_obj['cdc_acm'][last_item]['attributes'] = []
this_attribute = f'{keyname} {ca[keyname].get("value", "")} {ca[keyname].get("description", "")}'.strip()
i_desc_obj['cdc_acm'][last_item]['attributes'].append(this_attribute)
continue
i_desc_obj['cdc_acm'].update(ca)
del i_desc_obj['cdc_acm'][keyname]['_state']
for cu in self.cdc_union_list:
keyname = tuple(cu.keys())[0]
if '_state' in cu[keyname] and cu[keyname]['_state']['bus_idx'] == idx and cu[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
# is this a top level value or an attribute?
if cu[keyname]['_state']['attribute_value']:
last_item = cu[keyname]['_state']['last_item']
if 'attributes' not in i_desc_obj['cdc_union'][last_item]:
i_desc_obj['cdc_union'][last_item]['attributes'] = []
this_attribute = f'{keyname} {cu[keyname].get("value", "")} {cu[keyname].get("description", "")}'.strip()
i_desc_obj['cdc_union'][last_item]['attributes'].append(this_attribute)
continue
i_desc_obj['cdc_union'].update(cu)
del i_desc_obj['cdc_union'][keyname]['_state']
for hidd in self.hid_device_descriptor_list:
keyname = tuple(hidd.keys())[0]
if '_state' in hidd[keyname] and hidd[keyname]['_state']['bus_idx'] == idx and hidd[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
# is this a top level value or an attribute?
if hidd[keyname]['_state']['attribute_value']:
last_item = hidd[keyname]['_state']['last_item']
if 'attributes' not in i_desc_obj['hid_device_descriptor'][last_item]:
i_desc_obj['hid_device_descriptor'][last_item]['attributes'] = []
this_attribute = f'{keyname} {hidd[keyname].get("value", "")} {hidd[keyname].get("description", "")}'.strip()
i_desc_obj['hid_device_descriptor'][last_item]['attributes'].append(this_attribute)
continue
i_desc_obj['hid_device_descriptor'].update(hidd)
del i_desc_obj['hid_device_descriptor'][keyname]['_state']
# Not Implemented: Report Descriptors (need more samples)
# for rd in self.report_descriptors_list:
# keyname = tuple(rd.keys())[0]
# if '_state' in rd[keyname] and rd[keyname]['_state']['bus_idx'] == idx and rd[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
# i_desc_obj['hid_device_descriptor']['report_descriptors'].update(rd)
# del i_desc_obj['hid_device_descriptor']['report_descriptors'][keyname]['_state']
# add endpoint_descriptor key if it doesn't exist and there are entries for this interface_descriptor
for endpoint_attrs in self.endpoint_descriptor_list:
keyname = tuple(endpoint_attrs.keys())[0]
if '_state' in endpoint_attrs[keyname] and endpoint_attrs[keyname]['_state']['bus_idx'] == idx and endpoint_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
i_desc_obj['endpoint_descriptors'] = []
# find max index for this endpoint_descriptor idx, then iterate over that range
e_desc_iters = -1
for endpoint_attrs in self.endpoint_descriptor_list:
keyname = tuple(endpoint_attrs.keys())[0]
if '_state' in endpoint_attrs[keyname] and endpoint_attrs[keyname]['_state']['bus_idx'] == idx and endpoint_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
e_desc_iters = endpoint_attrs[keyname]['_state']['endpoint_descriptor_idx']
# create the endpoint descriptor object
if e_desc_iters > -1:
for endpoint_idx in range(e_desc_iters + 1):
e_desc_obj = {}
for endpoint_attrs in self.endpoint_descriptor_list:
keyname = tuple(endpoint_attrs.keys())[0]
if '_state' in endpoint_attrs[keyname] and endpoint_attrs[keyname]['_state']['bus_idx'] == idx and endpoint_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx and endpoint_attrs[keyname]['_state']['endpoint_descriptor_idx'] == endpoint_idx:
# is this a top level value or an attribute?
if endpoint_attrs[keyname]['_state']['attribute_value']:
last_item = endpoint_attrs[keyname]['_state']['last_item']
if 'attributes' not in e_desc_obj[last_item]:
e_desc_obj[last_item]['attributes'] = []
this_attribute = f'{keyname} {endpoint_attrs[keyname].get("value", "")} {endpoint_attrs[keyname].get("description", "")}'.strip()
e_desc_obj[last_item]['attributes'].append(this_attribute)
continue
e_desc_obj.update(endpoint_attrs)
del endpoint_attrs[keyname]['_state']
i_desc_obj['endpoint_descriptors'].append(e_desc_obj)
if self.endpoint_descriptors._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
i_desc_obj['endpoint_descriptors'] = []
i_desc_obj['endpoint_descriptors'].extend(
self.endpoint_descriptors._get_objects_list(idx, iface_idx)
)
# add the object to the list of interface descriptors
self.output_line['device_descriptor']['configuration_descriptor']['interface_descriptors'].append(i_desc_obj)
for hd in self.hub_descriptor_list:
keyname = tuple(hd.keys())[0]
if '_state' in hd[keyname] and hd[keyname]['_state']['bus_idx'] == idx:
# is this a top level value or an attribute?
if hd[keyname]['_state']['attribute_value']:
last_item = hd[keyname]['_state']['last_item']
if 'attributes' not in self.output_line['hub_descriptor'][last_item]:
self.output_line['hub_descriptor'][last_item]['attributes'] = []
this_attribute = f'{keyname} {hd[keyname].get("value", "")} {hd[keyname].get("description", "")}'.strip()
self.output_line['hub_descriptor'][last_item]['attributes'].append(this_attribute)
continue
self.output_line['hub_descriptor'].update(hd)
del self.output_line['hub_descriptor'][keyname]['_state']
# add final root-level keys
if self.hub_descriptor._entries_for_this_bus_exist(idx):
self.hub_descriptor._update_output(idx, self.output_line)
for hps in self.hub_port_status_list:
keyname = tuple(hps.keys())[0]
if '_state' in hps[keyname] and hps[keyname]['_state']['bus_idx'] == idx:
self.output_line['hub_descriptor']['hub_port_status'].update(hps)
del self.output_line['hub_descriptor']['hub_port_status'][keyname]['_state']
for dq in self.device_qualifier_list:
keyname = tuple(dq.keys())[0]
if '_state' in dq[keyname] and dq[keyname]['_state']['bus_idx'] == idx:
self.output_line['device_qualifier'].update(dq)
del self.output_line['device_qualifier'][keyname]['_state']
for ds in self.device_status_list:
if '_state' in ds and ds['_state']['bus_idx'] == idx:
self.output_line['device_status'].update(ds)
del self.output_line['device_status']['_state']

View File

@@ -71,6 +71,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['file']
__version__ = info.version

View File

@@ -235,6 +235,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['mdadm']
tags = ['command']
__version__ = info.version

View File

@@ -20,7 +20,7 @@ Schema:
"filesystem": string,
"mount_point": string,
"type": string,
"access": [
"options": [
string
]
}
@@ -34,7 +34,7 @@ Example:
"filesystem": "sysfs",
"mount_point": "/sys",
"type": "sysfs",
"access": [
"options": [
"rw",
"nosuid",
"nodev",
@@ -46,7 +46,7 @@ Example:
"filesystem": "proc",
"mount_point": "/proc",
"type": "proc",
"access": [
"options": [
"rw",
"nosuid",
"nodev",
@@ -58,7 +58,7 @@ Example:
"filesystem": "udev",
"mount_point": "/dev",
"type": "devtmpfs",
"access": [
"options": [
"rw",
"nosuid",
"relatime",
@@ -75,12 +75,13 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.7'
version = '1.8'
description = '`mount` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'freebsd']
compatible = ['linux', 'darwin', 'freebsd', 'aix']
magic_commands = ['mount']
tags = ['command']
__version__ = info.version
@@ -137,10 +138,38 @@ def _linux_parse(data):
output_line['filesystem'] = parsed_line[0]
output_line['mount_point'] = parsed_line[2]
output_line['type'] = parsed_line[4]
output_line['options'] = parsed_line[5].lstrip('(').rstrip(')').split(',')
options = parsed_line[5].lstrip('(').rstrip(')').split(',')
output.append(output_line)
output_line['options'] = options
return output
def _aix_parse(data):
output = []
# AIX mount command starts with these headers:
# node mounted mounted over vfs date options
# -------- --------------- --------------- ------ ------------ ---------------
# Remove them
data.pop(0)
data.pop(0)
for entry in data:
output_line = {}
parsed_line = entry.split()
# AIX mount entries have the remote node as the zeroth element. If the
# mount is local, the zeroth element is the filesystem instead. We can
# detect this by the lenth of the list. For local mounts, length is 7,
# and for remote mounts, the length is 8. In the remote case, pop off
# the zeroth element. Then parsed_line has a consistent format.
if len(parsed_line) == 8:
parsed_line.pop(0)
output_line['filesystem'] = parsed_line[0]
output_line['mount_point'] = parsed_line[1]
output_line['type'] = parsed_line[2]
output_line['options'] = parsed_line[6].lstrip('(').rstrip(')').split(',')
output.append(output_line)
@@ -170,9 +199,12 @@ def parse(data, raw=False, quiet=False):
if jc.utils.has_data(data):
# check for OSX output
# check for OSX and AIX output
if ' type ' not in cleandata[0]:
raw_output = _osx_parse(cleandata)
if 'node' in cleandata[0]:
raw_output = _aix_parse(cleandata)
else:
raw_output = _osx_parse(cleandata)
else:
raw_output = _linux_parse(cleandata)

View File

@@ -122,6 +122,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['mpstat']
tags = ['command']
__version__ = info.version

View File

@@ -106,6 +106,7 @@ class info():
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
tags = ['command']
streaming = True

View File

@@ -361,6 +361,7 @@ class info():
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'freebsd']
magic_commands = ['netstat']
tags = ['command']
__version__ = info.version

Some files were not shown because too many files have changed in this diff Show More