hf mf sim: remove mention of non-existing option
[RRG-proxmark3.git] / client / pyscripts / pm3_help2list.py
blob2798782bbe1a0d9b9ae0e46d8ab6716fb09ad84d
1 #!/usr/bin/env python3
2 """
3 PM3 Help 2 List
5 This script takes the full text help output from the PM3 client and converts it to a list to be used for readline autocomplete.
7 It is based on pm3_help2JSON.py by
8 Original Authors / Maintainers:
9 - Samuel Windall
11 This version
12 - Iceman
14 Note:
15 This script is used as a helper script to generate the pm3line_vocabulary.h file.
16 It need a working proxmark3 client to extract the help text.
18 Ie: this script can't be used inside the normal build sequence.
19 """
21 import re
22 import datetime
23 import argparse
24 import logging
26 ##############################################################################
27 # Script version data: (Please increment when making updates)
29 APP_NAME = 'PM3Help2List'
31 VERSION_MAJOR = 1
32 VERSION_MINOR = 0
34 ##############################################################################
35 # Main Application Code:
38 def main():
39 """The main function for the script"""
40 args = build_arg_parser().parse_args()
41 logging_format = '%(message)s'
42 if args.debug:
43 logging.basicConfig(level=logging.DEBUG, format=logging_format)
44 else:
45 logging.basicConfig(level=logging.WARN, format=logging_format)
46 logging.info(f'{get_version()} starting...')
47 help_text = args.input_file.read()
48 command_data = parse_all_command_data(help_text)
50 args.output_file.write("""//-----------------------------------------------------------------------------
51 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
53 // This program is free software: you can redistribute it and/or modify
54 // it under the terms of the GNU General Public License as published by
55 // the Free Software Foundation, either version 3 of the License, or
56 // (at your option) any later version.
58 // This program is distributed in the hope that it will be useful,
59 // but WITHOUT ANY WARRANTY; without even the implied warranty of
60 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
61 // GNU General Public License for more details.
63 // See LICENSE.txt for the text of the license.
64 //-----------------------------------------------------------------------------
65 // readline auto complete utilities
66 //-----------------------------------------------------------------------------
68 #ifndef PM3LINE_VOCABULARY_H__
69 #define PM3LINE_VOCABULARY_H__
71 #ifdef __cplusplus
72 extern "C" {
73 #endif
75 #include <stdbool.h>
77 typedef struct vocabulary_s {
78 bool offline;
79 const char *name;
80 } vocabulary_t;
82 const static vocabulary_t vocabulary[] = {\n""")
84 for key, values in command_data.items():
85 offline = 0
86 if (values['offline'] == True):
87 offline = 1
89 cmd = values['command']
91 args.output_file.write(' {{ {}, "{}" }},\n'.format(offline, cmd))
93 args.output_file.write(""" {0, NULL}\n};
95 #ifdef __cplusplus
97 #endif
99 #endif
100 """)
102 logging.info(f'{get_version()} completed!')
105 def build_arg_parser():
106 """Build the argument parser for reading the program arguments"""
107 parser = argparse.ArgumentParser()
108 parser.add_argument('input_file', type=argparse.FileType('r'), help='Source of full text help from the PM3 client.')
109 parser.add_argument('output_file', type=argparse.FileType('w'), help='Destination for list output.')
110 parser.add_argument('--version', '-v', action='version', version=get_version(), help='Version data about this app.')
111 parser.add_argument('--debug', '-d', action='store_true', help='Log debug messages.')
112 return parser
115 def build_help_regex():
116 """The regex uses to parse the full text output of help data from the pm3 client."""
117 # Reads the divider followed by the command itself
118 re_command = r'-{87}\n(?P<command>.+)\n'
120 # Reads if the command is available offline
121 re_offline = r'available offline: (?P<offline>yes|no)\n+'
123 return re.compile(re_command+re_offline, re.MULTILINE);
126 def parse_all_command_data(help_text):
127 """Turns the full text output of help data from the pm3 client into a list of dictionaries"""
128 command_dicts = {}
129 # Strip out ANSI escape sequences
130 help_text = remove_ansi_escape_codes(help_text)
131 # Find all commands in the full text help output
132 matches = build_help_regex().finditer(help_text)
133 for match in matches:
134 # Turn a match into a dictionary with keys for the extracted fields
135 command_object = parse_command_data(match)
136 # Store this command against its name for easy lookup
137 command_dicts[command_object['command']] = command_object
138 return command_dicts
141 def parse_command_data(match):
142 """Turns a regex match of a command in the help text and converts it into a dictionary"""
143 logging.info('Parsing new command...')
144 # Get and clean the command string
145 command = remove_extra_whitespace(match.group('command'))
146 logging.info(f' Command: {command}')
148 # Get the online status as a boolean. Note: the regex only picks up 'yes' or 'no' so this check is safe.
149 offline = (match.group('offline') == 'yes')
150 logging.debug(f' Offline: {offline}')
152 # Construct the command dictionary
153 command_data = {
154 'command': command,
155 'offline': offline,
157 logging.info('Completed parsing command!')
158 return command_data
161 ##############################################################################
162 # Helper Functions:
165 def get_version():
166 """Get the version string for this script"""
167 return f'{APP_NAME} v{VERSION_MAJOR}.{VERSION_MINOR:02}'
170 def remove_ansi_escape_codes(text):
171 """Remove ANSI escape sequences that may be left in the text."""
172 re_ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]')
173 return re_ansi_escape.sub('', str(text)).lower()
176 def remove_extra_whitespace(text):
177 """Removes extra whitespace that may be in the text."""
178 # Ensure input is a string
179 text = str(text)
180 # Remove whitespace from the start and end of the text
181 text = text.strip()
182 # Deduplicate spaces in the string
183 text = re.sub(r' +', ' ', text)
184 return text
187 def text_to_oneliner(text):
188 """Converts a multi line string into a single line string and removes extra whitespace"""
189 # Ensure input is a string
190 text = str(text)
191 # Replace newlines with spaces
192 text = re.sub(r'\n+', ' ', text)
193 # Remove the extra whitespace
194 text = remove_extra_whitespace(text)
195 return text
198 def text_to_list(text):
199 """Converts a multi line string into a list of lines and removes extra whitespace"""
200 # Ensure input is a string
201 text = str(text)
202 # Get all the lines
203 lines = text.strip().split('\n')
204 # For each line clean up any extra whitespace
205 return [remove_extra_whitespace(line) for line in lines]
208 ##############################################################################
209 # Application entrypoint:
211 if __name__ == '__main__':
212 main()