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:
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.
26 ##############################################################################
27 # Script version data: (Please increment when making updates)
29 APP_NAME
= 'PM3Help2List'
34 ##############################################################################
35 # Main Application Code:
39 """The main function for the script"""
40 args
= build_arg_parser().parse_args()
41 logging_format
= '%(message)s'
43 logging
.basicConfig(level
=logging
.DEBUG
, format
=logging_format
)
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__
77 typedef struct vocabulary_s {
82 const static vocabulary_t vocabulary[] = {\n""")
84 for key
, values
in command_data
.items():
86 if (values
['offline'] == True):
89 cmd
= values
['command']
91 args
.output_file
.write(' {{ {}, "{}" }},\n'.format(offline
, cmd
))
93 args
.output_file
.write(""" {0, NULL}\n};
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.')
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"""
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
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
157 logging
.info('Completed parsing command!')
161 ##############################################################################
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
180 # Remove whitespace from the start and end of the text
182 # Deduplicate spaces in the string
183 text
= re
.sub(r
' +', ' ', 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
191 # Replace newlines with spaces
192 text
= re
.sub(r
'\n+', ' ', text
)
193 # Remove the extra whitespace
194 text
= remove_extra_whitespace(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
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__':