2022-01-11 06:53:01 +08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// 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 <stdlib.h>
|
|
|
|
#include <stdio.h> // for Mingw readline and for getline
|
|
|
|
#include <string.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#if defined(HAVE_READLINE)
|
|
|
|
#include <readline/readline.h>
|
|
|
|
#include <readline/history.h>
|
2022-01-11 06:53:01 +08:00
|
|
|
#elif defined(HAVE_LINENOISE)
|
|
|
|
#include "linenoise.h"
|
2022-01-11 06:53:01 +08:00
|
|
|
#endif
|
|
|
|
#include "pm3line_vocabulory.h"
|
|
|
|
#include "pm3_cmd.h"
|
|
|
|
#include "ui.h" // g_session
|
|
|
|
#include "util.h" // str_ndup
|
|
|
|
|
|
|
|
#if defined(HAVE_READLINE)
|
|
|
|
|
2022-01-24 03:02:57 +08:00
|
|
|
static char *rl_command_generator(const char *text, int state) {
|
2022-01-11 06:53:01 +08:00
|
|
|
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.
|
2022-01-24 03:02:57 +08:00
|
|
|
if ((g_session.pm3_present == false) && (vocabulory[index].offline == false)) {
|
2022-01-11 06:53:01 +08:00
|
|
|
index++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
2022-01-24 03:02:57 +08:00
|
|
|
if (strncmp(command, rl_line_buffer, rlen) == 0) {
|
2022-01-11 06:53:01 +08:00
|
|
|
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;
|
2022-01-24 03:02:57 +08:00
|
|
|
return rl_completion_matches(text, rl_command_generator);
|
2022-01-11 06:53:01 +08:00
|
|
|
}
|
|
|
|
|
2022-01-11 06:53:01 +08:00
|
|
|
#elif defined(HAVE_LINENOISE)
|
|
|
|
static void ln_command_completion(const char *text, linenoiseCompletions *lc) {
|
|
|
|
int index = 0;
|
|
|
|
const char *prev_match = "";
|
|
|
|
size_t prev_match_len = 0;
|
|
|
|
size_t len = strlen(text);
|
|
|
|
const char *command;
|
|
|
|
while ((command = vocabulory[index].name)) {
|
|
|
|
|
|
|
|
// When no pm3 device present
|
|
|
|
// and the command is not available offline,
|
|
|
|
// we skip it.
|
2022-02-05 19:32:50 +08:00
|
|
|
if ((g_session.pm3_present == false) && (vocabulory[index].offline == false)) {
|
2022-01-11 06:53:01 +08:00
|
|
|
index++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
2022-02-05 19:32:50 +08:00
|
|
|
if (strncmp(command, text, len) == 0) {
|
2022-01-11 06:53:01 +08:00
|
|
|
const char *space = strstr(command + len, " ");
|
|
|
|
if (space != NULL) {
|
2022-02-05 19:32:50 +08:00
|
|
|
if ((prev_match_len == 0) || (strncmp(prev_match, command, prev_match_len < space - command ? prev_match_len : space - command) != 0)) {
|
2022-01-11 06:53:01 +08:00
|
|
|
linenoiseAddCompletion(lc, str_ndup(command, space - command + 1));
|
|
|
|
prev_match = command;
|
|
|
|
prev_match_len = space - command + 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
linenoiseAddCompletion(lc, command);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-11 06:53:01 +08:00
|
|
|
#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
|
|
|
|
|
2022-01-24 03:02:57 +08:00
|
|
|
void pm3line_install_signals(void) {
|
2022-01-11 06:53:01 +08:00
|
|
|
# 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
|
2022-01-11 06:53:01 +08:00
|
|
|
#elif defined(HAVE_LINENOISE)
|
|
|
|
linenoiseInstallWindowChangeHandler();
|
|
|
|
linenoiseSetCompletionCallback(ln_command_completion);
|
2022-01-11 06:53:01 +08:00
|
|
|
#endif // HAVE_READLINE
|
|
|
|
}
|
|
|
|
|
|
|
|
char *pm3line_read(const char *s) {
|
|
|
|
#if defined(HAVE_READLINE)
|
|
|
|
return readline(s);
|
2022-01-11 06:53:01 +08:00
|
|
|
#elif defined(HAVE_LINENOISE)
|
|
|
|
return linenoise(s);
|
2022-01-11 06:53:01 +08:00
|
|
|
#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;
|
|
|
|
}
|
2022-01-11 06:53:01 +08:00
|
|
|
#elif defined(HAVE_LINENOISE)
|
|
|
|
if (linenoiseHistoryLoad(path) == 0) {
|
|
|
|
return PM3_SUCCESS;
|
|
|
|
} else {
|
|
|
|
return PM3_ESOFT;
|
|
|
|
}
|
2022-01-11 06:53:01 +08:00
|
|
|
#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);
|
|
|
|
}
|
2022-01-11 06:53:01 +08:00
|
|
|
#elif defined(HAVE_LINENOISE)
|
|
|
|
// linenoiseHistoryAdd takes already care of duplicate entries
|
|
|
|
linenoiseHistoryAdd(line);
|
2022-01-11 06:53:01 +08:00
|
|
|
#else
|
|
|
|
(void) line;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void pm3line_flush_history(void) {
|
|
|
|
if (g_session.history_path) {
|
|
|
|
#if defined(HAVE_READLINE)
|
|
|
|
write_history(g_session.history_path);
|
2022-01-11 06:53:01 +08:00
|
|
|
#elif defined(HAVE_LINENOISE)
|
|
|
|
linenoiseHistorySave(g_session.history_path);
|
2022-01-11 06:53:01 +08:00
|
|
|
#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()
|