1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // API to abstract Readline / Linenoise support
17 //-----------------------------------------------------------------------------
21 #include <stdio.h> // for Mingw readline and for getline
24 #if defined(HAVE_READLINE)
25 #include <readline/readline.h>
26 #include <readline/history.h>
27 #elif defined(HAVE_LINENOISE)
28 #include "linenoise.h"
30 #include "pm3line_vocabulary.h"
32 #include "ui.h" // g_session
33 #include "util.h" // str_ndup
35 #if defined(HAVE_READLINE)
37 static char *rl_command_generator(const char *text
, int state
) {
40 size_t rlen
= strlen(rl_line_buffer
);
48 while ((command
= vocabulary
[index
].name
)) {
50 // When no pm3 device present
51 // and the command is not available offline,
53 if ((g_session
.pm3_present
== false) && (vocabulary
[index
].offline
== false)) {
60 if (strncmp(command
, rl_line_buffer
, rlen
) == 0) {
61 const char *next
= command
+ (rlen
- len
);
62 const char *space
= strstr(next
, " ");
64 return str_ndup(next
, space
- next
);
73 static char **rl_command_completion(const char *text
, int start
, int end
) {
74 rl_attempted_completion_over
= 0;
75 return rl_completion_matches(text
, rl_command_generator
);
78 #elif defined(HAVE_LINENOISE)
79 static void ln_command_completion(const char *text
, linenoiseCompletions
*lc
) {
81 const char *prev_match
= "";
82 size_t prev_match_len
= 0;
83 size_t len
= strlen(text
);
85 while ((command
= vocabulary
[index
].name
)) {
87 // When no pm3 device present
88 // and the command is not available offline,
90 if ((g_session
.pm3_present
== false) && (vocabulary
[index
].offline
== false)) {
97 if (strncmp(command
, text
, len
) == 0) {
98 const char *space
= strstr(command
+ len
, " ");
100 if ((prev_match_len
== 0) || (strncmp(prev_match
, command
, prev_match_len
< space
- command
? prev_match_len
: space
- command
) != 0)) {
101 linenoiseAddCompletion(lc
, str_ndup(command
, space
- command
+ 1));
102 prev_match
= command
;
103 prev_match_len
= space
- command
+ 1;
106 linenoiseAddCompletion(lc
, command
);
111 #endif // HAVE_READLINE
115 static bool WINAPI terminate_handler(DWORD t) {
116 if (t == CTRL_C_EVENT) {
124 static struct sigaction gs_old_sigint_action
;
125 static void sigint_handler(int signum
) {
129 sigaction(SIGINT
, &gs_old_sigint_action
, NULL
);
130 pm3line_flush_history();
142 void pm3line_install_signals(void) {
144 // SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminate_handler, true);
146 struct sigaction action
;
147 memset(&action
, 0, sizeof(action
));
148 action
.sa_handler
= &sigint_handler
;
149 sigaction(SIGINT
, &action
, &gs_old_sigint_action
);
152 #if defined(HAVE_READLINE)
153 rl_catch_signals
= 1;
155 #endif // HAVE_READLINE
158 void pm3line_init(void) {
159 #if defined(HAVE_READLINE)
160 /* initialize history */
162 rl_readline_name
= "PM3";
163 rl_attempted_completion_function
= rl_command_completion
;
165 // don't hook signal in MINGW
166 #if defined(__MINGW32__) || defined(__MINGW64__)
168 rl_getc_function
= getc
;
171 pm3line_install_signals();
173 #ifdef RL_STATE_READCMD
174 rl_extend_line_buffer(1024);
175 #endif // RL_STATE_READCMD
176 #elif defined(HAVE_LINENOISE)
177 linenoiseInstallWindowChangeHandler();
178 linenoiseSetCompletionCallback(ln_command_completion
);
179 #endif // HAVE_READLINE
182 char *pm3line_read(const char *s
) {
183 #if defined(HAVE_READLINE)
185 #elif defined(HAVE_LINENOISE)
192 if ((ret
= getline(&answer
, &anslen
, stdin
)) < 0) {
193 // TODO this happens also when kbd_enter_pressed() is used, with a key pressed or not
194 printf("DEBUG: getline returned %i", ret
);
202 void pm3line_free(void *ref
) {
206 void pm3line_update_prompt(const char *prompt
) {
207 #if defined(HAVE_READLINE)
208 rl_set_prompt(prompt
);
209 rl_forced_update_display();
215 int pm3line_load_history(const char *path
) {
216 #if defined(HAVE_READLINE)
217 if (read_history(path
) == 0) {
222 #elif defined(HAVE_LINENOISE)
223 if (linenoiseHistoryLoad(path
) == 0) {
234 void pm3line_add_history(const char *line
) {
235 #if defined(HAVE_READLINE)
236 HIST_ENTRY
*entry
= history_get(history_length
);
237 // add if not identical to latest recorded line
238 if ((!entry
) || (strcmp(entry
->line
, line
) != 0)) {
241 #elif defined(HAVE_LINENOISE)
242 // linenoiseHistoryAdd takes already care of duplicate entries
243 linenoiseHistoryAdd(line
);
249 void pm3line_flush_history(void) {
250 if (g_session
.history_path
) {
251 #if defined(HAVE_READLINE)
252 write_history(g_session
.history_path
);
253 #elif defined(HAVE_LINENOISE)
254 linenoiseHistorySave(g_session
.history_path
);
255 #endif // HAVE_READLINE
256 free(g_session
.history_path
);
257 g_session
.history_path
= NULL
;
261 void pm3line_check(int (check
)(void)) {
262 #if defined(HAVE_READLINE)
263 rl_event_hook
= check
;
270 // src/ui.c print_progress()