From c672d1c174a42ce44544dfaec0ebcf3c30fd7acf Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 9 Feb 2021 16:03:00 -0800 Subject: [PATCH] initial working hciconfig parser --- CHANGELOG | 3 + EXAMPLES.md | 126 ++++++++++++ README.md | 1 + docgen.sh | 1 + docs/parsers/hciconfig.md | 358 +++++++++++++++++++++++++++++++++ jc/parsers/hciconfig.py | 412 ++++++++++++++++++++++++++++++++++++-- man/jc.1 | 22 +- 7 files changed, 902 insertions(+), 21 deletions(-) create mode 100644 docs/parsers/hciconfig.md diff --git a/CHANGELOG b/CHANGELOG index 2eed170d..5321cd46 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ jc changelog +20210209 v1.14.3 +- Add hciconfig parser tested on linux + 20210205 v1.14.2 - Update dig parser to fix cases where there are spaces in the answer data (e.g. TXT records) diff --git a/EXAMPLES.md b/EXAMPLES.md index 4adf945a..cc3ed2e8 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -915,6 +915,132 @@ md5sum * | jc --hashsum -p # or: jc -p md5sum * } ] ``` +### hciconfig +```bash +hciconfig -a | jc --hciconfig -p # or: jc -p hciconfig -a +``` +```json +[ + { + "device": "hci0", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": 310, + "acl_mtu_packets": 10, + "sco_mtu": 64, + "sco_mtu_packets": 8, + "state": [ + "UP", + "RUNNING" + ], + "rx_bytes": 13905869, + "rx_acl": 0, + "rx_sco": 0, + "rx_events": 393300, + "rx_errors": 0, + "tx_bytes": 62629, + "tx_acl": 0, + "tx_sco": 0, + "tx_commands": 3893, + "tx_errors": 0, + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ], + "name": "CSR8510 A10", + "class": "0x000000", + "service_classes": null, + "device_class": "Miscellaneous", + "hci_version": "4.0 (0x6)", + "hci_revision": "0x22bb", + "lmp_version": "4.0 (0x6)", + "lmp_subversion": "0x22bb", + "manufacturer": "Cambridge Silicon Radio (10)" + }, + { + "device": "hci1", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": 310, + "acl_mtu_packets": 10, + "sco_mtu": 64, + "sco_mtu_packets": 8, + "state": [ + "DOWN" + ], + "rx_bytes": 4388363, + "rx_acl": 0, + "rx_sco": 0, + "rx_events": 122021, + "rx_errors": 0, + "tx_bytes": 52350, + "tx_acl": 0, + "tx_sco": 0, + "tx_commands": 3480, + "tx_errors": 2, + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ] + } +] +``` ### history ```bash history | jc --history -p diff --git a/README.md b/README.md index 91181793..1b858de1 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ The JSON output can be compact (default) or pretty formatted with the `-p` optio - `--gshadow` enables the `/etc/gshadow` file parser - `--hash` enables the `hash` command parser - `--hashsum` enables the `hashsum` command parser (`md5`, `md5sum`, `shasum`, `sha1sum`, `sha224sum`, `sha256sum`, `sha384sum`, `sha512sum`) +- `--hciconfig` enables the `hciconfig` command parser - `--history` enables the `history` command parser - `--hosts` enables the `/etc/hosts` file parser - `--id` enables the `id` command parser diff --git a/docgen.sh b/docgen.sh index 167e967d..9a802c61 100755 --- a/docgen.sh +++ b/docgen.sh @@ -26,6 +26,7 @@ pydocmd simple jc.parsers.group+ > ../docs/parsers/group.md pydocmd simple jc.parsers.gshadow+ > ../docs/parsers/gshadow.md pydocmd simple jc.parsers.hash+ > ../docs/parsers/hash.md pydocmd simple jc.parsers.hashsum+ > ../docs/parsers/hashsum.md +pydocmd simple jc.parsers.hciconfig+ > ../docs/parsers/hciconfig.md pydocmd simple jc.parsers.history+ > ../docs/parsers/history.md pydocmd simple jc.parsers.hosts+ > ../docs/parsers/hosts.md pydocmd simple jc.parsers.id+ > ../docs/parsers/id.md diff --git a/docs/parsers/hciconfig.md b/docs/parsers/hciconfig.md new file mode 100644 index 00000000..46c209de --- /dev/null +++ b/docs/parsers/hciconfig.md @@ -0,0 +1,358 @@ + +# jc.parsers.hciconfig +jc - JSON CLI output utility `hciconfig` command output parser + +Usage (cli): + + $ hciconfig | jc --hciconfig + + or + + $ jc hciconfig + +Usage (module): + + import jc.parsers.hciconfig + result = jc.parsers.hciconfig.parse(hciconfig_command_output) + +Compatibility: + + 'linux' + +Examples: + + $ hciconfig -a | jc --hciconfig -p + [ + { + "device": "hci0", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": 310, + "acl_mtu_packets": 10, + "sco_mtu": 64, + "sco_mtu_packets": 8, + "state": [ + "UP", + "RUNNING" + ], + "rx_bytes": 13905869, + "rx_acl": 0, + "rx_sco": 0, + "rx_events": 393300, + "rx_errors": 0, + "tx_bytes": 62629, + "tx_acl": 0, + "tx_sco": 0, + "tx_commands": 3893, + "tx_errors": 0, + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ], + "name": "CSR8510 A10", + "class": "0x000000", + "service_classes": null, + "device_class": "Miscellaneous", + "hci_version": "4.0 (0x6)", + "hci_revision": "0x22bb", + "lmp_version": "4.0 (0x6)", + "lmp_subversion": "0x22bb", + "manufacturer": "Cambridge Silicon Radio (10)" + }, + { + "device": "hci1", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": 310, + "acl_mtu_packets": 10, + "sco_mtu": 64, + "sco_mtu_packets": 8, + "state": [ + "DOWN" + ], + "rx_bytes": 4388363, + "rx_acl": 0, + "rx_sco": 0, + "rx_events": 122021, + "rx_errors": 0, + "tx_bytes": 52350, + "tx_acl": 0, + "tx_sco": 0, + "tx_commands": 3480, + "tx_errors": 2, + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ] + } + ] + + $ hciconfig -a | jc --hciconfig -p -r + [ + { + "device": "hci0", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": "310", + "acl_mtu_packets": "10", + "sco_mtu": "64", + "sco_mtu_packets": "8", + "state": [ + "UP", + "RUNNING" + ], + "rx_bytes": "13905869", + "rx_acl": "0", + "rx_sco": "0", + "rx_events": "393300", + "rx_errors": "0", + "tx_bytes": "62629", + "tx_acl": "0", + "tx_sco": "0", + "tx_commands": "3893", + "tx_errors": "0", + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ], + "name": "CSR8510 A10", + "class": "0x000000", + "service_classes": [ + "Unspecified" + ], + "device_class": "Miscellaneous", + "hci_version": "4.0 (0x6)", + "hci_revision": "0x22bb", + "lmp_version": "4.0 (0x6)", + "lmp_subversion": "0x22bb", + "manufacturer": "Cambridge Silicon Radio (10)" + }, + { + "device": "hci1", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": "310", + "acl_mtu_packets": "10", + "sco_mtu": "64", + "sco_mtu_packets": "8", + "state": [ + "DOWN" + ], + "rx_bytes": "4388363", + "rx_acl": "0", + "rx_sco": "0", + "rx_events": "122021", + "rx_errors": "0", + "tx_bytes": "52350", + "tx_acl": "0", + "tx_sco": "0", + "tx_commands": "3480", + "tx_errors": "2", + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ] + } + ] + + +## info +```python +info() +``` + + +## process +```python +process(proc_data) +``` + +Final processing to conform to the schema. + +Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + +Returns: + + List of Dictionaries. Structured data with the following schema: + + [ + { + "device": string, + "type": string, + "bus": string, + "bd_address": string, + "acl_mtu": integer, + "acl_mtu_packets": integer, + "sco_mtu": integer, + "sco_mtu_packets": integer, + "state": [ + string + ], + "rx_bytes": integer, + "rx_acl": integer, + "rx_sco": integer, + "rx_events": integer, + "rx_errors": integer, + "tx_bytes": integer, + "tx_acl": integer, + "tx_sco": integer, + "tx_commands": integer, + "tx_errors": integer, + "features": [ + string + ], + "packet_type": [ + string + ], + "link_policy": [ + string + ], + "link_mode": [ + string + ], + "name": string, + "class": string, + "service_classes": [ + string # 'Unspecified' is null + ], + "device_class": string, + "hci_version": string, + "hci_revision": string, + "lmp_version": string, + "lmp_subversion": string, + "manufacturer": string + } + ] + + +## parse +```python +parse(data, raw=False, quiet=False) +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + +Returns: + + List of Dictionaries. Raw or processed structured data. + diff --git a/jc/parsers/hciconfig.py b/jc/parsers/hciconfig.py index 7812c3e9..ad85f045 100644 --- a/jc/parsers/hciconfig.py +++ b/jc/parsers/hciconfig.py @@ -1,7 +1,5 @@ """jc - JSON CLI output utility `hciconfig` command output parser -<> - Usage (cli): $ hciconfig | jc --hciconfig @@ -21,11 +19,251 @@ Compatibility: Examples: - $ hciconfig | jc --hciconfig -p - [] + $ hciconfig -a | jc --hciconfig -p + [ + { + "device": "hci0", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": 310, + "acl_mtu_packets": 10, + "sco_mtu": 64, + "sco_mtu_packets": 8, + "state": [ + "UP", + "RUNNING" + ], + "rx_bytes": 13905869, + "rx_acl": 0, + "rx_sco": 0, + "rx_events": 393300, + "rx_errors": 0, + "tx_bytes": 62629, + "tx_acl": 0, + "tx_sco": 0, + "tx_commands": 3893, + "tx_errors": 0, + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ], + "name": "CSR8510 A10", + "class": "0x000000", + "service_classes": null, + "device_class": "Miscellaneous", + "hci_version": "4.0 (0x6)", + "hci_revision": "0x22bb", + "lmp_version": "4.0 (0x6)", + "lmp_subversion": "0x22bb", + "manufacturer": "Cambridge Silicon Radio (10)" + }, + { + "device": "hci1", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": 310, + "acl_mtu_packets": 10, + "sco_mtu": 64, + "sco_mtu_packets": 8, + "state": [ + "DOWN" + ], + "rx_bytes": 4388363, + "rx_acl": 0, + "rx_sco": 0, + "rx_events": 122021, + "rx_errors": 0, + "tx_bytes": 52350, + "tx_acl": 0, + "tx_sco": 0, + "tx_commands": 3480, + "tx_errors": 2, + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ] + } + ] - $ hciconfig | jc --hciconfig -p -r - [] + $ hciconfig -a | jc --hciconfig -p -r + [ + { + "device": "hci0", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": "310", + "acl_mtu_packets": "10", + "sco_mtu": "64", + "sco_mtu_packets": "8", + "state": [ + "UP", + "RUNNING" + ], + "rx_bytes": "13905869", + "rx_acl": "0", + "rx_sco": "0", + "rx_events": "393300", + "rx_errors": "0", + "tx_bytes": "62629", + "tx_acl": "0", + "tx_sco": "0", + "tx_commands": "3893", + "tx_errors": "0", + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ], + "name": "CSR8510 A10", + "class": "0x000000", + "service_classes": [ + "Unspecified" + ], + "device_class": "Miscellaneous", + "hci_version": "4.0 (0x6)", + "hci_revision": "0x22bb", + "lmp_version": "4.0 (0x6)", + "lmp_subversion": "0x22bb", + "manufacturer": "Cambridge Silicon Radio (10)" + }, + { + "device": "hci1", + "type": "Primary", + "bus": "USB", + "bd_address": "00:1A:7D:DA:71:13", + "acl_mtu": "310", + "acl_mtu_packets": "10", + "sco_mtu": "64", + "sco_mtu_packets": "8", + "state": [ + "DOWN" + ], + "rx_bytes": "4388363", + "rx_acl": "0", + "rx_sco": "0", + "rx_events": "122021", + "rx_errors": "0", + "tx_bytes": "52350", + "tx_acl": "0", + "tx_sco": "0", + "tx_commands": "3480", + "tx_errors": "2", + "features": [ + "0xff", + "0xff", + "0x8f", + "0xfe", + "0xdb", + "0xff", + "0x5b", + "0x87" + ], + "packet_type": [ + "DM1", + "DM3", + "DM5", + "DH1", + "DH3", + "DH5", + "HV1", + "HV2", + "HV3" + ], + "link_policy": [ + "RSWITCH", + "HOLD", + "SNIFF", + "PARK" + ], + "link_mode": [ + "SLAVE", + "ACCEPT" + ] + } + ] """ import jc.utils @@ -95,19 +333,33 @@ def process(proc_data): "name": string, "class": string, "service_classes": [ - string + string # 'Unspecified' is null ], "device_class": string, "hci_version": string, - "revision": string, + "hci_revision": string, "lmp_version": string, - "subversion": string, + "lmp_subversion": string, "manufacturer": string } ] """ - # rebuild output for added semantic information + for entry in proc_data: + + # integers + int_list = ['acl_mtu', 'acl_mtu_packets', 'sco_mtu', 'sco_mtu_packets', 'rx_bytes', 'rx_acl', 'rx_sco', + 'rx_events', 'rx_errors', 'tx_bytes', 'tx_acl', 'tx_sco', 'tx_commands', 'tx_errors'] + for key in int_list: + if key in entry: + try: + entry[key] = int(entry[key]) + except (ValueError): + entry[key] = None + + if 'service_classes' in entry and len(entry['service_classes']) == 1 and 'Unspecified' in entry['service_classes']: + entry['service_classes'] = None + return proc_data @@ -129,12 +381,148 @@ def parse(data, raw=False, quiet=False): jc.utils.compatibility(__name__, info.compatible) raw_output = [] + device_object = {} + line_count = 0 if jc.utils.has_data(data): for line in filter(None, data.splitlines()): - # parse the content - pass + line_count += 1 + # print(line) + + # start of a new device object + # hci0: Type: Primary Bus: USB + if not line.startswith(' '): + if device_object: + raw_output.append(device_object) + device_object = {} + line_count = 1 + + line = line.replace(':', '') + line = line.split() + device_object['device'] = line[0] + device_object['type'] = line[2] + device_object['bus'] = line[4] + + continue + + # BD Address: 00:50:56:E7:46:1A ACL MTU: 8192:128 SCO MTU: 64:128 + if line.startswith(' ') and ' BD Address:' in line: + line = line.split() + device_object['bd_address'] = line[2] + device_object['acl_mtu'] = line[5].split(':')[0] + device_object['acl_mtu_packets'] = line[5].split(':')[1] + device_object['sco_mtu'] = line[8].split(':')[0] + device_object['sco_mtu_packets'] = line[8].split(':')[1] + + continue + + # UP RUNNING (always line 3) + if line_count == 3: + device_object['state'] = line.split() + + continue + + # RX bytes:1307 acl:0 sco:0 events:51 errors:0 + if line.startswith(' ') and ' RX bytes:' in line: + line = line.replace(':', ' ') + line = line.split() + device_object['rx_bytes'] = line[2] + device_object['rx_acl'] = line[4] + device_object['rx_sco'] = line[6] + device_object['rx_events'] = line[8] + device_object['rx_errors'] = line[10] + + continue + + # TX bytes:1200 acl:0 sco:0 commands:51 errors:0 + if line.startswith(' ') and ' TX bytes:' in line: + line = line.replace(':', ' ') + line = line.split() + device_object['tx_bytes'] = line[2] + device_object['tx_acl'] = line[4] + device_object['tx_sco'] = line[6] + device_object['tx_commands'] = line[8] + device_object['tx_errors'] = line[10] + + continue + + # Features: 0xff 0xff 0x8f 0xfe 0x83 0xe1 0x08 0x80 + if line.startswith(' ') and line.lstrip().startswith('Features:'): + device_object['features'] = line.split()[1:] + + continue + + # Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 + if line.startswith(' ') and line.lstrip().startswith('Packet type:'): + device_object['packet_type'] = line.split()[2:] + + continue + + # Link policy: RSWITCH HOLD SNIFF PARK + if line.startswith(' ') and line.lstrip().startswith('Link policy:'): + device_object['link_policy'] = line.split()[2:] + + continue + + # Link mode: SLAVE ACCEPT + if line.startswith(' ') and line.lstrip().startswith('Link mode:'): + device_object['link_mode'] = line.split()[2:] + + continue + + # Name: 'kbrazil-ubuntu' + if line.startswith(' ') and line.lstrip().startswith('Name:'): + device_object['name'] = line.split(maxsplit=1)[1][1:-1] + + continue + + # Class: 0x000000 + if line.startswith(' ') and line.lstrip().startswith('Class:'): + device_object['class'] = line.split(maxsplit=1)[1] + + continue + + # Service Classes: Unspecified + if line.startswith(' ') and line.lstrip().startswith('Service Classes:'): + device_object['service_classes'] = line.split()[2:] + + continue + + # Device Class: Miscellaneous, + if line.startswith(' ') and line.lstrip().startswith('Device Class:'): + dev_class = line.split()[2] + if dev_class.endswith(','): + dev_class = dev_class[0:-1] + + device_object['device_class'] = dev_class + + continue + + # HCI Version: 4.0 (0x6) Revision: 0x22bb + if line.startswith(' ') and line.lstrip().startswith('HCI Version:'): + line = line.split() + device_object['hci_version'] = ' '.join(line[2:4]) + device_object['hci_revision'] = line[5] + + continue + + # LMP Version: 4.0 (0x6) Subversion: 0x22bb + if line.startswith(' ') and line.lstrip().startswith('LMP Version:'): + line = line.split() + device_object['lmp_version'] = ' '.join(line[2:4]) + device_object['lmp_subversion'] = line[5] + + continue + + # Manufacturer: Cambridge Silicon Radio (10) + if line.startswith(' ') and line.lstrip().startswith('Manufacturer:'): + device_object['manufacturer'] = line.split(maxsplit=1)[1] + + continue + + if device_object: + raw_output.append(device_object) if raw: return raw_output diff --git a/man/jc.1 b/man/jc.1 index d0f2e3a9..f1ff39da 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -1,4 +1,4 @@ -.TH jc 1 2021-01-05 1.14.1 "JSON CLI output utility" +.TH jc 1 2021-01-05 1.14.3 "JSON CLI output utility" .SH NAME jc \- JSONifies the output of many CLI tools and file-types .SH SYNOPSIS @@ -92,6 +92,18 @@ fstab file parser /etc/gshadow file parser .TP .B +\fB--hash\fP +hash BASH builtin command parser +.TP +.B +\fB--hashsum\fP +md5, md5sum, shasum, sha1sum, sha224sum, sha256sum, sha384sum, and sha512sum command parser +.TP +.B +\fB--hciconfig\fP +hciconfig command parser +.TP +.B \fB--history\fP history command parser .TP @@ -120,14 +132,6 @@ iptables command parser iw dev scan command parser .TP .B -\fB--hash\fP -hash BASH builtin command parser -.TP -.B -\fB--hashsum\fP -md5, md5sum, shasum, sha1sum, sha224sum, sha256sum, sha384sum, and sha512sum command parser -.TP -.B \fB--jobs\fP jobs command parser .TP