fix one too small
[RRG-proxmark3.git] / client / src / pm3line.c
blob5270a5fbeea134cb7a89e868fa6bb98d5fd945fa
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
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.
8 //
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 //-----------------------------------------------------------------------------
19 #include "pm3line.h"
20 #include <stdlib.h>
21 #include <stdio.h> // for Mingw readline and for getline
22 #include <string.h>
23 #include <signal.h>
24 #if defined(HAVE_READLINE)
25 #include <readline/readline.h>
26 #include <readline/history.h>
27 #elif defined(HAVE_LINENOISE)
28 #include "linenoise.h"
29 #endif
30 #include "pm3line_vocabulary.h"
31 #include "pm3_cmd.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) {
38 static int index;
39 static size_t len;
40 size_t rlen = strlen(rl_line_buffer);
41 const char *command;
43 if (!state) {
44 index = 0;
45 len = strlen(text);
48 while ((command = vocabulary[index].name)) {
50 // When no pm3 device present
51 // and the command is not available offline,
52 // we skip it.
53 if ((g_session.pm3_present == false) && (vocabulary[index].offline == false)) {
54 index++;
55 continue;
58 index++;
60 if (strncmp(command, rl_line_buffer, rlen) == 0) {
61 const char *next = command + (rlen - len);
62 const char *space = strstr(next, " ");
63 if (space != NULL) {
64 return str_ndup(next, space - next);
66 return str_dup(next);
70 return NULL;
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) {
80 int index = 0;
81 const char *prev_match = "";
82 size_t prev_match_len = 0;
83 size_t len = strlen(text);
84 const char *command;
85 while ((command = vocabulary[index].name)) {
87 // When no pm3 device present
88 // and the command is not available offline,
89 // we skip it.
90 if ((g_session.pm3_present == false) && (vocabulary[index].offline == false)) {
91 index++;
92 continue;
95 index++;
97 if (strncmp(command, text, len) == 0) {
98 const char *space = strstr(command + len, " ");
99 if (space != NULL) {
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;
105 } else {
106 linenoiseAddCompletion(lc, command);
111 #endif // HAVE_READLINE
113 # if defined(_WIN32)
115 static bool WINAPI terminate_handler(DWORD t) {
116 if (t == CTRL_C_EVENT) {
117 flush_history();
118 return true;
120 return false;
123 # else
124 static struct sigaction gs_old_sigint_action;
125 static void sigint_handler(int signum) {
127 switch (signum) {
128 case SIGINT: {
129 sigaction(SIGINT, &gs_old_sigint_action, NULL);
130 pm3line_flush_history();
131 kill(0, SIGINT);
132 break;
134 default: {
135 break;
140 #endif
142 void pm3line_install_signals(void) {
143 # if defined(_WIN32)
144 // SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminate_handler, true);
145 # else
146 struct sigaction action;
147 memset(&action, 0, sizeof(action));
148 action.sa_handler = &sigint_handler;
149 sigaction(SIGINT, &action, &gs_old_sigint_action);
150 # endif
152 #if defined(HAVE_READLINE)
153 rl_catch_signals = 1;
154 rl_set_signals();
155 #endif // HAVE_READLINE
158 void pm3line_init(void) {
159 #if defined(HAVE_READLINE)
160 /* initialize history */
161 using_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__)
167 #else
168 rl_getc_function = getc;
169 #endif
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)
184 return readline(s);
185 #elif defined(HAVE_LINENOISE)
186 return linenoise(s);
187 #else
188 printf("%s", s);
189 char *answer = NULL;
190 size_t anslen = 0;
191 int ret;
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);
195 free(answer);
196 answer = NULL;
198 return answer;
199 #endif
202 void pm3line_free(void *ref) {
203 free(ref);
206 void pm3line_update_prompt(const char *prompt) {
207 #if defined(HAVE_READLINE)
208 rl_set_prompt(prompt);
209 rl_forced_update_display();
210 #else
211 (void) prompt;
212 #endif
215 int pm3line_load_history(const char *path) {
216 #if defined(HAVE_READLINE)
217 if (read_history(path) == 0) {
218 return PM3_SUCCESS;
219 } else {
220 return PM3_ESOFT;
222 #elif defined(HAVE_LINENOISE)
223 if (linenoiseHistoryLoad(path) == 0) {
224 return PM3_SUCCESS;
225 } else {
226 return PM3_ESOFT;
228 #else
229 (void) path;
230 return PM3_ENOTIMPL;
231 #endif
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)) {
239 add_history(line);
241 #elif defined(HAVE_LINENOISE)
242 // linenoiseHistoryAdd takes already care of duplicate entries
243 linenoiseHistoryAdd(line);
244 #else
245 (void) line;
246 #endif
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;
264 #else
265 check();
266 #endif
269 // TODO:
270 // src/ui.c print_progress()