[keymap] support for /dev/clipboard in :write commands

The copy hotkey is now implemented in the keymap and the
':write-*' commands were enhanced to recognize /dev/clipboard
as a special file name for writing to the system clipboard.
pull/545/head
Timothy Stack 6 years ago
parent 0b822739ea
commit db6c619e4e

@ -1,5 +1,9 @@
lnav v0.8.5:
Features:
* The ":write-*" commands will now accept "/dev/clipboard" as a file name
that writes to the system clipboard.
Interface Changes:
* The auto-complete behavior in the prompt has been modified to fall back
to a fuzzy search if the prefix search finds no matches. For example,

@ -131,11 +131,14 @@ Output
* append-to <file> - Append any bookmarked lines in the current view to the
given file.
* write-to <file> - Overwrite the given file with any bookmarked lines in
the current view. Use '-' to write the lines to the terminal.
the current view. Use '-' to write the lines to the terminal and '/dev/clipboard'
to write to the system clipboard.
* write-csv-to <file> - Write SQL query results to the given file in CSV format.
Use '-' to write the lines to the terminal.
Use '-' to write the lines to the terminal and '/dev/clipboard' to write to
the system clipboard.
* write-json-to <file> - Write SQL query results to the given file in JSON
format. Use '-' to write the lines to the terminal.
format. Use '-' to write the lines to the terminal and '/dev/clipboard'
to write to the system clipboard..
* pipe-to <shell-cmd> - Pipe the bookmarked lines in the current view to a
shell command and open the output in lnav.
* pipe-line-to <shell-cmd> - Pipe the top line in the current view to a shell

@ -101,45 +101,6 @@ public:
vector<logline_value> lh_line_values;
};
/* XXX For one, this code is kinda crappy. For two, we should probably link
* directly with X so we don't need to have xclip installed and it'll work if
* we're ssh'd into a box.
*/
static void copy_to_xclip(void)
{
textview_curses *tc = lnav_data.ld_view_stack.back();
bookmark_vector<vis_line_t> &bv =
tc->get_bookmarks()[&textview_curses::BM_USER];
bookmark_vector<vis_line_t>::iterator iter;
auto_mem<FILE> pfile(pclose);
int line_count = 0;
string line;
pfile = open_clipboard(CT_GENERAL);
if (!pfile.in()) {
alerter::singleton().chime();
lnav_data.ld_rl_view->set_value(
"error: Unable to copy to clipboard. "
"Make sure xclip or pbcopy is installed.");
return;
}
for (iter = bv.begin(); iter != bv.end(); iter++) {
tc->grep_value_for_line(*iter, line);
fprintf(pfile, "%s\n", line.c_str());
line_count += 1;
}
char buffer[128];
snprintf(buffer, sizeof(buffer),
"Copied " ANSI_BOLD("%d") " lines to the clipboard",
line_count);
lnav_data.ld_rl_view->set_value(buffer);
}
void handle_paging_key(int ch)
{
if (lnav_data.ld_view_stack.empty()) {
@ -210,12 +171,6 @@ void handle_paging_key(int ch)
}
break;
case 'c':
copy_to_xclip();
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
C, "to clear marked messages"));
break;
case 'C':
if (lss) {
lss->text_clear_marks(&textview_curses::BM_USER);
@ -673,24 +628,20 @@ void handle_paging_key(int ch)
if (tc == &lnav_data.ld_views[LNV_DB]) {
db_label_source &dls = lnav_data.ld_db_row_source;
for (vector<db_label_source::header_meta>::iterator iter = dls.dls_headers.begin();
iter != dls.dls_headers.end();
++iter) {
if (!iter->hm_graphable) {
for (auto &dls_header : dls.dls_headers) {
if (!dls_header.hm_graphable) {
continue;
}
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND,
"numeric-colname",
iter->hm_name);
dls_header.hm_name);
}
}
else {
for (vector<logline_value>::iterator iter = ldh.ldh_line_values.begin();
iter != ldh.ldh_line_values.end();
++iter) {
const logline_value_stats *stats = iter->lv_format->stats_for_value(
iter->lv_name);
for (auto &ldh_line_value : ldh.ldh_line_values) {
const logline_value_stats *stats = ldh_line_value.lv_format->stats_for_value(
ldh_line_value.lv_name);
if (stats == NULL) {
continue;
@ -698,14 +649,13 @@ void handle_paging_key(int ch)
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND,
"numeric-colname",
iter->lv_name.to_string());
ldh_line_value.lv_name.to_string());
}
}
for (vector<string>::iterator iter = ldh.ldh_namer->cn_names.begin();
iter != ldh.ldh_namer->cn_names.end();
++iter) {
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "colname", *iter);
for (auto &cn_name : ldh.ldh_namer->cn_names) {
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "colname",
cn_name);
}
for (auto iter : ldh.ldh_namer->cn_builtin_names) {
if (iter == "col") {
@ -834,7 +784,7 @@ void handle_paging_key(int ch)
break;
case 't':
if (lnav_data.ld_text_source.current_file() == NULL) {
if (lnav_data.ld_text_source.current_file() == nullptr) {
alerter::singleton().chime();
lnav_data.ld_rl_view->set_value("No text files loaded");
}

@ -8,7 +8,8 @@
"keymap_def_hist_view": "Press ${ansi_bold}i${ansi_norm}/${ansi_bold}I${ansi_norm} to switch to the histogram view",
"keymap_def_text_view": "Press ${ansi_bold}t${ansi_norm} to switch to the text view",
"keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view",
"keymap_def_zoom": "Press ${ansi_bold}z${ansi_norm}/${ansi_bold}z${ansi_norm} to zoom in/out"
"keymap_def_zoom": "Press ${ansi_bold}z${ansi_norm}/${ansi_bold}z${ansi_norm} to zoom in/out",
"keymap_def_clear": "Press ${ansi_bold}C${ansi_norm} to clear marked messages"
},
"keymap_def": {
"default": {
@ -49,6 +50,11 @@
":next-mark query"
],
"x63": [
":eval :alt-msg ${keymap_def_clear}",
":write-to /dev/clipboard"
],
"x67": [":goto 0"],
"x6d": [
":mark",

@ -53,6 +53,7 @@
#include "relative_time.hh"
#include "log_search_table.hh"
#include "shlex.hh"
#include "sysclip.hh"
#include "yajl/api/yajl_parse.h"
#include "db_sub_source.hh"
@ -500,10 +501,11 @@ static void json_write_row(yajl_gen handle, int row)
static string com_save_to(exec_context &ec, string cmdline, vector<string> &args)
{
FILE *outfile = NULL, *toclose = NULL;
FILE *outfile = nullptr, *toclose = nullptr;
const char *mode = "";
string fn, retval;
bool to_term = false;
int (*closer)(FILE *) = fclose;
if (args.empty()) {
args.emplace_back("filename");
@ -564,7 +566,7 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
outfile = tmpfile();
toclose = outfile;
}
else if (split_args[0] == "-") {
else if (split_args[0] == "-" || split_args[0] == "/dev/stdout") {
if (lnav_data.ld_output_stack.empty()) {
outfile = stdout;
nodelay(lnav_data.ld_window, 0);
@ -586,13 +588,24 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
lnav_data.ld_stdout_used = true;
}
}
else if ((outfile = fopen(split_args[0].c_str(), mode)) == NULL) {
else if (split_args[0] == "/dev/clipboard") {
toclose = outfile = open_clipboard(CT_GENERAL);
closer = pclose;
if (!outfile) {
alerter::singleton().chime();
return "error: Unable to copy to clipboard. "
"Make sure xclip or pbcopy is installed.";
}
}
else if ((outfile = fopen(split_args[0].c_str(), mode)) == nullptr) {
return "error: unable to open file -- " + split_args[0];
}
else {
toclose = outfile;
}
int line_count = 0;
if (args[0] == "write-csv-to") {
std::vector<std::vector<const char *> >::iterator row_iter;
std::vector<const char *>::iterator iter;
@ -629,6 +642,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
first = false;
}
fprintf(outfile, "\n");
line_count += 1;
}
}
else if (args[0] == "write-cols-to") {
@ -648,6 +663,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
text_sub_source::RF_RAW);
fputs(line.c_str(), outfile);
fputc('\n', outfile);
line_count += 1;
}
}
else if (args[0] == "write-json-to") {
@ -696,6 +713,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
fputs(*iter, outfile);
}
fprintf(outfile, "\n");
line_count += 1;
}
} else {
bool wrapped = tc->get_word_wrap();
@ -714,6 +733,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
log_perror(write(STDOUT_FILENO, lr.substr(al.get_string()),
lr.sublen(al.get_string())));
log_perror(write(STDOUT_FILENO, "\n", 1));
line_count += 1;
}
tc->set_word_wrap(wrapped);
@ -731,6 +752,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
}
tc->grep_value_for_line(*iter, line);
fprintf(outfile, "%s\n", line.c_str());
line_count += 1;
}
}
@ -756,13 +779,15 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
.truncate_to(10);
lnav_data.ld_preview_status_source.get_description()
.set_value("First lines of file: %s", fn.c_str());
} else {
retval = "Wrote " + to_string(line_count) + " line to " + split_args[0];
}
if (toclose != NULL) {
fclose(toclose);
if (toclose != nullptr) {
closer(toclose);
}
outfile = NULL;
outfile = nullptr;
return "";
return retval;
}
static string com_pipe_to(exec_context &ec, string cmdline, vector<string> &args)
@ -834,13 +859,11 @@ static string com_pipe_to(exec_context &ec, string cmdline, vector<string> &args
sql_strftime(tmp_str, sizeof(tmp_str), ldh.ldh_line->get_timeval());
setenv("log_time", tmp_str, 1);
setenv("log_path", ldh.ldh_file->get_filename().c_str(), 1);
for (vector<logline_value>::iterator iter = ldh.ldh_line_values.begin();
iter != ldh.ldh_line_values.end();
++iter) {
setenv(iter->lv_name.get(), iter->to_string().c_str(), 1);
for (auto &ldh_line_value : ldh.ldh_line_values) {
setenv(ldh_line_value.lv_name.get(),
ldh_line_value.to_string().c_str(), 1);
}
data_parser::element_list_t::iterator iter =
ldh.ldh_parser->dp_pairs.begin();
auto iter = ldh.ldh_parser->dp_pairs.begin();
for (size_t lpc = 0; lpc < ldh.ldh_parser->dp_pairs.size(); lpc++, ++iter) {
std::string colname = ldh.ldh_parser->get_element_string(
iter->e_sub_elements->front());

@ -51,7 +51,7 @@ static clip_command *get_commands()
static clip_command X_CMDS[] = {
{ { "xclip -i > /dev/null 2>&1",
"xclip -o < /dev/null 2>/dev/null" } },
{ { NULL, NULL } },
{ { nullptr, nullptr } },
};
if (system("which pbcopy > /dev/null 2>&1") == 0) {
return OSX_CMDS;
@ -59,7 +59,7 @@ static clip_command *get_commands()
if (system("which xclip > /dev/null 2>&1") == 0) {
return X_CMDS;
}
return NULL;
return nullptr;
}
/* XXX For one, this code is kinda crappy. For two, we should probably link
@ -70,9 +70,9 @@ FILE *open_clipboard(clip_type_t type, clip_op_t op)
{
const char *mode = op == CO_WRITE ? "w" : "r";
clip_command *cc = get_commands();
FILE *pfile = NULL;
FILE *pfile = nullptr;
if (cc != NULL && cc[type].cc_cmd[op] != NULL) {
if (cc != nullptr && cc[type].cc_cmd[op] != nullptr) {
pfile = popen(cc[type].cc_cmd[op], mode);
}

Loading…
Cancel
Save