diff --git a/Makefile b/Makefile index 32c9b26aa..f446e880e 100644 --- a/Makefile +++ b/Makefile @@ -287,7 +287,7 @@ style: [ -x client/proxmark3 ] && client/proxmark3 --fulltext | sed 's#com[0-9]#/dev/ttyacm0#'|python3 client/pyscripts/pm3_help2json.py - doc/commands.json # Update the readline autocomplete autogenerated code - [ -x client/proxmark3 ] && client/proxmark3 --fulltext | python3 client/pyscripts/pm3_help2list.py - client/src/rl_vocabulory.h + [ -x client/proxmark3 ] && client/proxmark3 --fulltext | python3 client/pyscripts/pm3_help2list.py - client/src/pm3line_vocabulory.h # Detecting weird codepages and tabs. diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 26d5c16bd..a84415814 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -340,6 +340,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/pm3.c ${PM3_ROOT}/client/src/pm3_binlib.c ${PM3_ROOT}/client/src/pm3_bitlib.c + ${PM3_ROOT}/client/src/pm3line.c ${PM3_ROOT}/client/src/prng.c ${PM3_ROOT}/client/src/scandir.c ${PM3_ROOT}/client/src/scripting.c @@ -578,6 +579,7 @@ endif (NOT APPLE) if (NOT JANSSON_FOUND) set(ADDITIONAL_LNK pm3rrg_rdv4_jansson ${ADDITIONAL_LNK}) endif (NOT JANSSON_FOUND) + if (NOT WHEREAMI_FOUND) set(ADDITIONAL_LNK pm3rrg_rdv4_whereami ${ADDITIONAL_LNK}) endif (NOT WHEREAMI_FOUND) diff --git a/client/Makefile b/client/Makefile index 0b74919c2..1a11b2fe2 100644 --- a/client/Makefile +++ b/client/Makefile @@ -635,6 +635,7 @@ SRCS = mifare/aiddesfire.c \ pm3_bitlib.c \ preferences.c \ prng.c \ + pm3line.c \ proxmark3.c \ scandir.c \ uart/uart_posix.c \ diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index 8e0e68621..ac455eaec 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -341,6 +341,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/pm3.c ${PM3_ROOT}/client/src/pm3_binlib.c ${PM3_ROOT}/client/src/pm3_bitlib.c + ${PM3_ROOT}/client/src/pm3line.c ${PM3_ROOT}/client/src/prng.c ${PM3_ROOT}/client/src/scandir.c ${PM3_ROOT}/client/src/scripting.c @@ -581,6 +582,7 @@ endif (NOT APPLE) if (NOT JANSSON_FOUND) set(ADDITIONAL_LNK pm3rrg_rdv4_jansson ${ADDITIONAL_LNK}) endif (NOT JANSSON_FOUND) + if (NOT WHEREAMI_FOUND) set(ADDITIONAL_LNK pm3rrg_rdv4_whereami ${ADDITIONAL_LNK}) endif (NOT WHEREAMI_FOUND) diff --git a/client/pyscripts/pm3_help2list.py b/client/pyscripts/pm3_help2list.py index 58830b7e6..7c27d23fe 100755 --- a/client/pyscripts/pm3_help2list.py +++ b/client/pyscripts/pm3_help2list.py @@ -12,7 +12,7 @@ This version - Iceman Note: - This script is used as a helper script to generate the rl_vocabulory.h file. + This script is used as a helper script to generate the pm3line_vocabulory.h file. It need a working proxmark3 client to extract the help text. Ie: this script can't be used inside the normal build sequence. @@ -65,22 +65,14 @@ def main(): // readline auto complete utilities //----------------------------------------------------------------------------- -#ifndef RL_VOCABULORY_H__ -#define RL_VOCABULORY_H__ +#ifndef PM3LINE_VOCABULORY_H__ +#define PM3LINE_VOCABULORY_H__ #ifdef __cplusplus extern "C" { #endif -#if defined(HAVE_READLINE) -#include -#include -#include -#include "ui.h" // g_session -#include "util.h" // str_ndup - -char* rl_command_generator(const char *text, int state); -char **rl_command_completion(const char *text, int start, int end); +#include typedef struct vocabulory_s { bool offline; @@ -100,50 +92,6 @@ const static vocabulory_t vocabulory[] = {\n""") args.output_file.write(""" {0, NULL}\n}; - -char **rl_command_completion(const char *text, int start, int end) { - rl_attempted_completion_over = 0; - return rl_completion_matches (text, rl_command_generator); -} - -char* rl_command_generator(const char *text, int state) { - static int index; - static size_t len; - size_t rlen = strlen(rl_line_buffer); - const char *command; - - if (!state) { - index = 0; - len = strlen(text); - } - - while ((command = vocabulory[index].name)) { - - // When no pm3 device present - // and the command is not available offline, - // we skip it. - if ((g_session.pm3_present == false) && (vocabulory[index].offline == false )) { - index++; - continue; - } - - index++; - - if (strncmp (command, rl_line_buffer, rlen) == 0) { - const char *next = command + (rlen - len); - const char *space = strstr(next, " "); - if (space != NULL) { - return str_ndup(next, space - next); - } - return str_dup(next); - } - } - - return NULL; -} - -#endif - #ifdef __cplusplus } #endif diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 16f5656a7..f00e58407 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -17,13 +17,9 @@ //----------------------------------------------------------------------------- #include "cmdhflegic.h" -#include // for Mingw readline #include // tolower -#ifdef HAVE_READLINE -#include -#endif - +#include "pm3line.h" // pm3line_read, pm3line_free #include "cliparser.h" #include "cmdparser.h" // command_t #include "comms.h" // clearCommandBuffer @@ -553,19 +549,9 @@ static int CmdLegicWrbl(const char *Cmd) { PrintAndLogEx(INFO, "#####################################"); const char *confirm = "Do you really want to continue? y(es)/n(o) : "; bool overwrite = false; -#ifdef HAVE_READLINE - char *answer = readline(confirm); + char *answer = pm3line_read(confirm); overwrite = (answer[0] == 'y' || answer[0] == 'Y'); -#else - PrintAndLogEx(NORMAL, "%s" NOLF, confirm); - char *answer = NULL; - size_t anslen = 0; - if (getline(&answer, &anslen, stdin) > 0) { - overwrite = (answer[0] == 'y' || answer[0] == 'Y'); - } - PrintAndLogEx(NORMAL, ""); -#endif - free(answer); + pm3line_free(answer); if (overwrite == false) { PrintAndLogEx(WARNING, "command cancelled"); return PM3_EOPABORTED; diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 55ad35917..991815e46 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -993,7 +993,7 @@ void pm3_version(bool verbose, bool oneliner) { PrintAndLogEx(NORMAL, "%s", temp); PrintAndLogEx(NORMAL, " compiled with............. " PM3CLIENTCOMPILER __VERSION__); PrintAndLogEx(NORMAL, " platform.................. " PM3HOSTOS " / " PM3HOSTARCH); -#ifdef HAVE_READLINE +#if defined(HAVE_READLINE) PrintAndLogEx(NORMAL, " Readline support.......... " _GREEN_("present")); #else PrintAndLogEx(NORMAL, " Readline support.......... " _YELLOW_("absent")); diff --git a/client/src/pm3line.c b/client/src/pm3line.c new file mode 100644 index 000000000..6d33e9990 --- /dev/null +++ b/client/src/pm3line.c @@ -0,0 +1,200 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// API to abstract Readline / Linenoise support +//----------------------------------------------------------------------------- + +#include "pm3line.h" +#include +#include // for Mingw readline and for getline +#include +#include +#if defined(HAVE_READLINE) +#include +#include +#endif +#include "pm3line_vocabulory.h" +#include "pm3_cmd.h" +#include "ui.h" // g_session +#include "util.h" // str_ndup + +#if defined(HAVE_READLINE) + +static char* rl_command_generator(const char *text, int state) { + static int index; + static size_t len; + size_t rlen = strlen(rl_line_buffer); + const char *command; + + if (!state) { + index = 0; + len = strlen(text); + } + + while ((command = vocabulory[index].name)) { + + // When no pm3 device present + // and the command is not available offline, + // we skip it. + if ((g_session.pm3_present == false) && (vocabulory[index].offline == false )) { + index++; + continue; + } + + index++; + + if (strncmp (command, rl_line_buffer, rlen) == 0) { + const char *next = command + (rlen - len); + const char *space = strstr(next, " "); + if (space != NULL) { + return str_ndup(next, space - next); + } + return str_dup(next); + } + } + + return NULL; +} + +static char **rl_command_completion(const char *text, int start, int end) { + rl_attempted_completion_over = 0; + return rl_completion_matches (text, rl_command_generator); +} + +#endif // HAVE_READLINE + +# if defined(_WIN32) +/* +static bool WINAPI terminate_handler(DWORD t) { + if (t == CTRL_C_EVENT) { + flush_history(); + return true; + } + return false; +} +*/ +# else +static struct sigaction gs_old_sigint_action; +static void sigint_handler(int signum) { + sigaction(SIGINT, &gs_old_sigint_action, NULL); + pm3line_flush_history(); + kill(0, SIGINT); +} +#endif + +void pm3line_install_signals(void){ +# if defined(_WIN32) +// SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminate_handler, true); +# else + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = &sigint_handler; + sigaction(SIGINT, &action, &gs_old_sigint_action); +# endif +#if defined(HAVE_READLINE) + rl_catch_signals = 1; + rl_set_signals(); +#endif // HAVE_READLINE +} + +void pm3line_init(void) { +#if defined(HAVE_READLINE) + /* initialize history */ + using_history(); + rl_readline_name = "PM3"; + rl_attempted_completion_function = rl_command_completion; + +#ifdef RL_STATE_READCMD + rl_extend_line_buffer(1024); +#endif // RL_STATE_READCMD +#endif // HAVE_READLINE +} + +char *pm3line_read(const char *s) { +#if defined(HAVE_READLINE) + return readline(s); +#else + printf("%s", s); + char *answer = NULL; + size_t anslen = 0; + int ret; + if ((ret = getline(&answer, &anslen, stdin)) < 0) { + // TODO this happens also when kbd_enter_pressed() is used, with a key pressed or not + printf("DEBUG: getline returned %i", ret); + free(answer); + answer = NULL; + } + return answer; +#endif +} + +void pm3line_free(void *ref) { + free(ref); +} + +void pm3line_update_prompt(const char *prompt) { +#if defined(HAVE_READLINE) + rl_set_prompt(prompt); + rl_forced_update_display(); +#else + (void) prompt; +#endif +} + +int pm3line_load_history(const char *path) { +#if defined(HAVE_READLINE) + if (read_history(path) == 0) { + return PM3_SUCCESS; + } else { + return PM3_ESOFT; + } +#else + (void) path; + return PM3_ENOTIMPL; +#endif +} + +void pm3line_add_history(const char *line) { +#if defined(HAVE_READLINE) + HIST_ENTRY *entry = history_get(history_length); + // add if not identical to latest recorded line + if ((!entry) || (strcmp(entry->line, line) != 0)) { + add_history(line); + } +#else + (void) line; +#endif +} + +void pm3line_flush_history(void) { + if (g_session.history_path) { +#if defined(HAVE_READLINE) + write_history(g_session.history_path); +#endif // HAVE_READLINE + free(g_session.history_path); + g_session.history_path = NULL; + } +} + +void pm3line_check(int (check)(void)) { +#if defined(HAVE_READLINE) + rl_event_hook = check; +#else + check(); +#endif +} + +// TODO: +// src/ui.c print_progress() diff --git a/client/src/pm3line.h b/client/src/pm3line.h new file mode 100644 index 000000000..50fd57afe --- /dev/null +++ b/client/src/pm3line.h @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// API to abstract Readline / Linenoise support +//----------------------------------------------------------------------------- + +#ifndef PM3LINE_H__ +#define PM3LINE_H__ + +void pm3line_init(void); +void pm3line_install_signals(void); +char *pm3line_read(const char* s); +void pm3line_free(void *ref); +void pm3line_update_prompt(const char *prompt); +int pm3line_load_history(const char *path); +void pm3line_add_history(const char *line); +void pm3line_flush_history(void); +void pm3line_check(int (check)(void)); + +#endif // PM3LINE_H__ diff --git a/client/src/rl_vocabulory.h b/client/src/pm3line_vocabulory.h similarity index 93% rename from client/src/rl_vocabulory.h rename to client/src/pm3line_vocabulory.h index ce1d1a7c2..d71e90895 100644 --- a/client/src/rl_vocabulory.h +++ b/client/src/pm3line_vocabulory.h @@ -16,22 +16,14 @@ // readline auto complete utilities //----------------------------------------------------------------------------- -#ifndef RL_VOCABULORY_H__ -#define RL_VOCABULORY_H__ +#ifndef PM3LINE_VOCABULORY_H__ +#define PM3LINE_VOCABULORY_H__ #ifdef __cplusplus extern "C" { #endif -#if defined(HAVE_READLINE) -#include -#include -#include -#include "ui.h" // g_session -#include "util.h" // str_ndup - -char* rl_command_generator(const char *text, int state); -char **rl_command_completion(const char *text, int start, int end); +#include typedef struct vocabulory_s { bool offline; @@ -722,50 +714,6 @@ const static vocabulory_t vocabulory[] = { {0, NULL} }; - -char **rl_command_completion(const char *text, int start, int end) { - rl_attempted_completion_over = 0; - return rl_completion_matches (text, rl_command_generator); -} - -char* rl_command_generator(const char *text, int state) { - static int index; - static size_t len; - size_t rlen = strlen(rl_line_buffer); - const char *command; - - if (!state) { - index = 0; - len = strlen(text); - } - - while ((command = vocabulory[index].name)) { - - // When no pm3 device present - // and the command is not available offline, - // we skip it. - if ((g_session.pm3_present == false) && (vocabulory[index].offline == false )) { - index++; - continue; - } - - index++; - - if (strncmp (command, rl_line_buffer, rlen) == 0) { - const char *next = command + (rlen - len); - const char *space = strstr(next, " "); - if (space != NULL) { - return str_ndup(next, space - next); - } - return str_dup(next); - } - } - - return NULL; -} - -#endif - #ifdef __cplusplus } #endif diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 67d1c6528..43649d22e 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -19,18 +19,12 @@ #include "proxmark3.h" #include -#include // for Mingw readline #include #include -#ifdef HAVE_READLINE -#include -#include -#include "rl_vocabulory.h" -#include -#endif #include #include // basename +#include "pm3line.h" #include "usart_defs.h" #include "util_posix.h" #include "proxgui.h" @@ -137,48 +131,16 @@ static int check_comm(void) { if (IsCommunicationThreadDead() && g_session.pm3_present) { PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") " mode. Use "_YELLOW_("\"hw connect\"") " to reconnect\n"); prompt_dev = PROXPROMPT_DEV_OFFLINE; -#ifdef HAVE_READLINE char prompt[PROXPROMPT_MAX_SIZE] = {0}; prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev); char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors); - rl_set_prompt(prompt_filtered); - rl_redisplay(); -#endif + pm3line_update_prompt(prompt_filtered); CloseProxmark(g_session.current_device); } msleep(10); return 0; } -#ifdef HAVE_READLINE -static void flush_history(void) { - if (g_session.history_path) { - write_history(g_session.history_path); - free(g_session.history_path); - g_session.history_path = NULL; - } -} - -# if defined(_WIN32) -/* -static bool WINAPI terminate_handler(DWORD t) { - if (t == CTRL_C_EVENT) { - flush_history(); - return true; - } - return false; -} -*/ -# else -static struct sigaction gs_old_sigint_action; -static void sigint_handler(int signum) { - sigaction(SIGINT, &gs_old_sigint_action, NULL); - flush_history(); - kill(0, SIGINT); -} -#endif - -#endif #if defined(_WIN32) static bool DetectWindowsAnsiSupport(void) { @@ -278,30 +240,22 @@ main_loop(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) { } } -#ifdef HAVE_READLINE g_session.history_path = NULL; if (g_session.incognito) { PrintAndLogEx(INFO, "No history will be recorded"); } else { + bool loaded_history = false; if (searchHomeFilePath(&g_session.history_path, NULL, PROXHISTORY, true) != PM3_SUCCESS) { - PrintAndLogEx(ERR, "No history will be recorded"); g_session.history_path = NULL; } else { - -# if defined(_WIN32) - // SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminate_handler, true); -# else - struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_handler = &sigint_handler; - sigaction(SIGINT, &action, &gs_old_sigint_action); -# endif - rl_catch_signals = 1; - rl_set_signals(); - read_history(g_session.history_path); + loaded_history = (pm3line_load_history(g_session.history_path) == PM3_SUCCESS); + } + if (loaded_history) { + pm3line_install_signals(); + } else { + PrintAndLogEx(ERR, "No history will be recorded"); } } -#endif // loops every time enter is pressed... while (1) { @@ -378,19 +332,14 @@ check_script: strcleanrn(script_cmd, script_cmd_len); goto check_script; } else { -#ifdef HAVE_READLINE - rl_event_hook = check_comm; -#else - check_comm(); -#endif + pm3line_check(check_comm); prompt_ctx = PROXPROMPT_CTX_INTERACTIVE; char prompt[PROXPROMPT_MAX_SIZE] = {0}; prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev); char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors); g_pendingPrompt = true; -#ifdef HAVE_READLINE - script_cmd = readline(prompt_filtered); + cmd = pm3line_read(prompt_filtered); #if defined(_WIN32) //Check if color support needs to be enabled again in case the window buffer did change g_session.supports_colors = DetectWindowsAnsiSupport(); @@ -405,18 +354,6 @@ check_script: strcleanrn(script_cmd, script_cmd_len); goto check_script; } -#else - printf("%s", prompt_filtered); - cmd = NULL; - size_t len = 0; - int ret; - if ((ret = getline(&cmd, &len, stdin)) < 0) { - // TODO this happens also when kbd_enter_pressed() is used, with a key pressed or not - printf("GETLINE ERR %i", ret); - free(cmd); - cmd = NULL; - } -#endif fflush(NULL); } } @@ -455,16 +392,10 @@ check_script: PrintAndLogEx(NORMAL, "%s%s", prompt_filtered, cmd); g_printAndLog = old_printAndLog; -#ifdef HAVE_READLINE // add to history if not from a script if (!current_cmdscriptfile()) { - HIST_ENTRY *entry = history_get(history_length); - // add if not identical to latest recorded cmd - if ((!entry) || (strcmp(entry->line, cmd) != 0)) { - add_history(cmd); - } + pm3line_add_history(cmd); } -#endif // process cmd g_pendingPrompt = false; int ret = CommandReceived(cmd); @@ -495,9 +426,7 @@ check_script: while (current_cmdscriptfile()) pop_cmdscriptfile(); -#ifdef HAVE_READLINE - flush_history(); -#endif + pm3line_flush_history(); if (cmd) { free(cmd); @@ -780,17 +709,7 @@ int main(int argc, char *argv[]) { char *port = NULL; uint32_t speed = 0; -#ifdef HAVE_READLINE - /* initialize history */ - using_history(); - - rl_readline_name = "PM3"; - rl_attempted_completion_function = rl_command_completion; - -#ifdef RL_STATE_READCMD - rl_extend_line_buffer(1024); -#endif // RL_STATE_READCMD -#endif // HAVE_READLINE + pm3line_init(); char exec_name[100] = {0}; strncpy(exec_name, basename(argv[0]), sizeof(exec_name) - 1); diff --git a/client/src/ui.c b/client/src/ui.c index 5dab29d1f..b5b1fc0f4 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -28,7 +28,7 @@ #include #include -#ifdef HAVE_READLINE +#if defined(HAVE_READLINE) //Load readline after stdio.h #include #endif @@ -643,7 +643,7 @@ void iceSimple_Filter(int *data, const size_t len, uint8_t k) { void print_progress(size_t count, uint64_t max, barMode_t style) { int cols = 100 + 35; -#ifdef HAVE_READLINE +#if defined(HAVE_READLINE) static int prev_cols = 0; int rows; rl_reset_screen_size(); // refresh Readline idea of the actual screen width