mirror of
https://github.com/tstack/lnav.git
synced 2024-08-17 00:40:30 +03:00
[cmd] replace wordexp with shlex
This commit is contained in:
parent
16e988d132
commit
ab62d27757
@ -91,6 +91,17 @@ check_output() {
|
||||
test_num=`expr ${test_num} \+ 1`
|
||||
}
|
||||
|
||||
check_output_ws() {
|
||||
diff -u - ${test_file_base}_${test_num}.tmp > ${test_file_base}_${test_num}.diff
|
||||
if test $? -ne 0; then
|
||||
echo $LAST_TEST
|
||||
echo $1
|
||||
cat ${test_file_base}_${test_num}.diff
|
||||
exit 1
|
||||
fi
|
||||
test_num=`expr ${test_num} \+ 1`
|
||||
}
|
||||
|
||||
test_err_filename() {
|
||||
echo ${test_file_base}_${test_num}.err
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "pcrecpp.h"
|
||||
#include "lnav.hh"
|
||||
#include "log_format_loader.hh"
|
||||
#include "shlex.hh"
|
||||
|
||||
#include "command_executor.hh"
|
||||
|
||||
@ -328,35 +329,32 @@ string execute_file(const string &path_and_args, bool multiline)
|
||||
{
|
||||
map<string, vector<string> > scripts;
|
||||
map<string, vector<string> >::iterator iter;
|
||||
static_root_mem<wordexp_t, wordfree> wordmem;
|
||||
vector<string> split_args;
|
||||
string msg, retval;
|
||||
shlex lexer(path_and_args);
|
||||
|
||||
log_info("Executing file: %s", path_and_args.c_str());
|
||||
|
||||
int exp_rc = wordexp(path_and_args.c_str(),
|
||||
wordmem.inout(),
|
||||
WRDE_NOCMD | WRDE_UNDEF);
|
||||
|
||||
if (!wordexperr(exp_rc, msg)) {
|
||||
retval = msg;
|
||||
if (!lexer.split(split_args, lnav_data.ld_local_vars.top())) {
|
||||
retval = "error: unable to parse path";
|
||||
}
|
||||
else if (wordmem->we_wordc == 0) {
|
||||
else if (split_args.empty()) {
|
||||
retval = "error: no script specified";
|
||||
}
|
||||
else {
|
||||
lnav_data.ld_local_vars.push(map<string, string>());
|
||||
|
||||
string script_name = wordmem->we_wordv[0];
|
||||
string script_name = split_args[0];
|
||||
map<string, string> &vars = lnav_data.ld_local_vars.top();
|
||||
char env_arg_name[32];
|
||||
string result, open_error = "file not found";
|
||||
|
||||
snprintf(env_arg_name, sizeof(env_arg_name), "%d", (int) wordmem->we_wordc - 1);
|
||||
snprintf(env_arg_name, sizeof(env_arg_name), "%d", (int) split_args.size() - 1);
|
||||
|
||||
vars["#"] = env_arg_name;
|
||||
for (unsigned int lpc = 0; lpc < wordmem->we_wordc; lpc++) {
|
||||
for (unsigned int lpc = 0; lpc < split_args.size(); lpc++) {
|
||||
snprintf(env_arg_name, sizeof(env_arg_name), "%d", lpc);
|
||||
vars[env_arg_name] = wordmem->we_wordv[lpc];
|
||||
vars[env_arg_name] = split_args[lpc];
|
||||
}
|
||||
|
||||
vector<string> paths_to_exec;
|
||||
|
@ -1925,7 +1925,7 @@ static void looper(void)
|
||||
"to switch to the next/previous file"));
|
||||
}
|
||||
if (lnav_data.ld_view_stack.top() == &lnav_data.ld_views[LNV_TEXT] &&
|
||||
lnav_data.ld_text_source.text_line_count() == 0 &&
|
||||
lnav_data.ld_text_source.empty() &&
|
||||
lnav_data.ld_log_source.text_line_count() > 0) {
|
||||
textview_curses *tc_log = &lnav_data.ld_views[LNV_LOG];
|
||||
lnav_data.ld_view_stack.pop();
|
||||
|
@ -29,7 +29,6 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <wordexp.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <string>
|
||||
@ -459,15 +458,13 @@ static string com_save_to(string cmdline, vector<string> &args)
|
||||
|
||||
fn = trim(remaining_args(cmdline, args));
|
||||
|
||||
static_root_mem<wordexp_t, wordfree> wordmem;
|
||||
vector<string> split_args;
|
||||
shlex lexer(fn);
|
||||
|
||||
int rc = wordexp(fn.c_str(), wordmem.inout(), WRDE_NOCMD | WRDE_UNDEF);
|
||||
|
||||
if (!wordexperr(rc, retval)) {
|
||||
return retval;
|
||||
if (!lexer.split(split_args, lnav_data.ld_local_vars.top())) {
|
||||
return "error: unable to parse arguments";
|
||||
}
|
||||
|
||||
if (wordmem->we_wordc > 1) {
|
||||
if (split_args.size() > 1) {
|
||||
return "error: more than one file name was matched";
|
||||
}
|
||||
|
||||
@ -495,7 +492,7 @@ static string com_save_to(string cmdline, vector<string> &args)
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(wordmem->we_wordv[0], "-") == 0) {
|
||||
if (split_args[0] == "-") {
|
||||
outfile = stdout;
|
||||
if (lnav_data.ld_flags & LNF_HEADLESS) {
|
||||
lnav_data.ld_stdout_used = true;
|
||||
@ -515,8 +512,8 @@ static string com_save_to(string cmdline, vector<string> &args)
|
||||
args[0].c_str());
|
||||
}
|
||||
}
|
||||
else if ((outfile = fopen(wordmem->we_wordv[0], mode)) == NULL) {
|
||||
return "error: unable to open file -- " + string(wordmem->we_wordv[0]);
|
||||
else if ((outfile = fopen(split_args[0].c_str(), mode)) == NULL) {
|
||||
return "error: unable to open file -- " + split_args[0];
|
||||
}
|
||||
|
||||
if (args[0] == "write-csv-to") {
|
||||
@ -1306,7 +1303,7 @@ static string com_open(string cmdline, vector<string> &args)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static_root_mem<wordexp_t, wordfree> wordmem;
|
||||
vector<string> word_exp;
|
||||
list<logfile *>::iterator file_iter;
|
||||
size_t colon_index;
|
||||
int top = 0;
|
||||
@ -1314,14 +1311,15 @@ static string com_open(string cmdline, vector<string> &args)
|
||||
|
||||
pat = trim(remaining_args(cmdline, args));
|
||||
|
||||
int rc = wordexp(pat.c_str(), wordmem.inout(), WRDE_NOCMD | WRDE_UNDEF);
|
||||
vector<string> split_args;
|
||||
shlex lexer(pat);
|
||||
|
||||
if (!wordexperr(rc, retval)) {
|
||||
return retval;
|
||||
if (!lexer.split(split_args, lnav_data.ld_local_vars.top())) {
|
||||
return "error: unable to parse arguments";
|
||||
}
|
||||
|
||||
for (size_t lpc = 0; lpc < wordmem->we_wordc; lpc++) {
|
||||
string fn = wordmem->we_wordv[lpc];
|
||||
for (size_t lpc = 0; lpc < split_args.size(); lpc++) {
|
||||
string fn = split_args[lpc];
|
||||
|
||||
if (startswith(fn, "pt:")) {
|
||||
lnav_data.ld_pt_search = fn;
|
||||
|
@ -516,6 +516,7 @@ void readline_shlex_highlighter(attr_line_t &al, int x)
|
||||
&view_curses::VC_STYLE,
|
||||
error_attrs));
|
||||
break;
|
||||
case ST_TILDE:
|
||||
case ST_ESCAPE:
|
||||
al.with_attr(string_attr(
|
||||
line_range(cap.c_begin, cap.c_end),
|
||||
@ -556,6 +557,8 @@ void readline_shlex_highlighter(attr_line_t &al, int x)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ST_WHITESPACE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
111
src/shlex.hh
111
src/shlex.hh
@ -38,6 +38,7 @@
|
||||
|
||||
enum shlex_token_t {
|
||||
ST_ERROR,
|
||||
ST_WHITESPACE,
|
||||
ST_ESCAPE,
|
||||
ST_DOUBLE_QUOTE_START,
|
||||
ST_DOUBLE_QUOTE_END,
|
||||
@ -45,6 +46,7 @@ enum shlex_token_t {
|
||||
ST_SINGLE_QUOTE_END,
|
||||
ST_VARIABLE_REF,
|
||||
ST_QUOTED_VARIABLE_REF,
|
||||
ST_TILDE,
|
||||
};
|
||||
|
||||
class shlex {
|
||||
@ -126,6 +128,33 @@ public:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '~':
|
||||
switch (this->s_state) {
|
||||
case STATE_NORMAL:
|
||||
cap_out.c_begin = this->s_index;
|
||||
this->s_index += 1;
|
||||
cap_out.c_end = this->s_index;
|
||||
token_out = ST_TILDE;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
switch (this->s_state) {
|
||||
case STATE_NORMAL:
|
||||
cap_out.c_begin = this->s_index;
|
||||
while (isspace(this->s_str[this->s_index])) {
|
||||
this->s_index += 1;
|
||||
}
|
||||
cap_out.c_end = this->s_index;
|
||||
token_out = ST_WHITESPACE;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -151,6 +180,9 @@ public:
|
||||
case ST_ESCAPE:
|
||||
result.append(1, this->s_str[cap.c_begin + 1]);
|
||||
break;
|
||||
case ST_WHITESPACE:
|
||||
result.append(&this->s_str[cap.c_begin], cap.length());
|
||||
break;
|
||||
case ST_VARIABLE_REF:
|
||||
case ST_QUOTED_VARIABLE_REF: {
|
||||
int extra = token == ST_VARIABLE_REF ? 0 : 1;
|
||||
@ -166,6 +198,17 @@ public:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ST_TILDE: {
|
||||
const char *home_dir = getenv("HOME");
|
||||
|
||||
if (home_dir != NULL) {
|
||||
result.append(home_dir);
|
||||
}
|
||||
else {
|
||||
result.append("~");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -177,6 +220,74 @@ public:
|
||||
return true;
|
||||
};
|
||||
|
||||
bool split(std::vector<std::string> &result, const std::map<std::string, std::string> &vars) {
|
||||
result.clear();
|
||||
|
||||
pcre_context::capture_t cap;
|
||||
shlex_token_t token;
|
||||
int last_index = 0;
|
||||
bool start_new = true;
|
||||
|
||||
while (isspace(this->s_str[this->s_index])) {
|
||||
this->s_index += 1;
|
||||
}
|
||||
while (this->tokenize(cap, token)) {
|
||||
if (start_new) {
|
||||
result.push_back("");
|
||||
start_new = false;
|
||||
}
|
||||
result.back().append(&this->s_str[last_index], cap.c_begin - last_index);
|
||||
switch (token) {
|
||||
case ST_ERROR:
|
||||
return false;
|
||||
case ST_ESCAPE:
|
||||
result.back().append(1, this->s_str[cap.c_begin + 1]);
|
||||
break;
|
||||
case ST_WHITESPACE:
|
||||
start_new = true;
|
||||
break;
|
||||
case ST_VARIABLE_REF:
|
||||
case ST_QUOTED_VARIABLE_REF: {
|
||||
int extra = token == ST_VARIABLE_REF ? 0 : 1;
|
||||
std::string var_name(&this->s_str[cap.c_begin + 1 + extra], cap.length() - 1 - extra * 2);
|
||||
std::map<std::string, std::string>::const_iterator local_var;
|
||||
const char *var_value = getenv(var_name.c_str());
|
||||
|
||||
if ((local_var = vars.find(var_name)) != vars.end()) {
|
||||
result.back().append(local_var->second);
|
||||
}
|
||||
else if (var_value != NULL) {
|
||||
result.back().append(var_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ST_TILDE: {
|
||||
const char *home_dir = getenv("HOME");
|
||||
|
||||
if (home_dir != NULL) {
|
||||
result.back().append(home_dir);
|
||||
}
|
||||
else {
|
||||
result.back().append("~");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
last_index = cap.c_end;
|
||||
}
|
||||
|
||||
if (last_index < this->s_len) {
|
||||
if (start_new || result.empty()) {
|
||||
result.push_back("");
|
||||
}
|
||||
result.back().append(&this->s_str[last_index], this->s_len - last_index);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
this->s_index = 0;
|
||||
this->s_state = STATE_NORMAL;
|
||||
|
@ -37,6 +37,7 @@ using namespace std;
|
||||
|
||||
const char *ST_TOKEN_NAMES[] = {
|
||||
"err",
|
||||
"wsp",
|
||||
"esc",
|
||||
"dst",
|
||||
"den",
|
||||
@ -44,6 +45,7 @@ const char *ST_TOKEN_NAMES[] = {
|
||||
"sen",
|
||||
"ref",
|
||||
"qrf",
|
||||
"til",
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@ -84,6 +86,14 @@ int main(int argc, char *argv[])
|
||||
if (lexer.eval(result, map<string, string>())) {
|
||||
printf("eval -- %s\n", result.c_str());
|
||||
}
|
||||
lexer.reset();
|
||||
std::vector<std::string> sresult;
|
||||
if (lexer.split(sresult, map<string, string>())) {
|
||||
printf("split:\n");
|
||||
for (int lpc = 0; lpc < sresult.size(); lpc++) {
|
||||
printf(" %d -- %s\n", lpc, sresult[lpc].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#! /bin/bash
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c "|${test_dir}/toplevel.lnav 123 456" \
|
||||
-c "|${test_dir}/toplevel.lnav 123 456 789" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_error_output "include toplevel.lnav" <<EOF
|
||||
@ -9,7 +9,7 @@ EOF
|
||||
|
||||
check_output "include toplevel.lnav" <<EOF
|
||||
toplevel here 123 456
|
||||
nested here nested.lnav abc
|
||||
nested here nested.lnav abc 789
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
|
||||
|
@ -5,104 +5,142 @@ export DEF='xyz'
|
||||
|
||||
run_test ./drive_shlexer '$FOO'
|
||||
|
||||
check_output "var ref" <<EOF
|
||||
check_output_ws "var ref" <<EOF
|
||||
\$FOO
|
||||
ref ^--^
|
||||
eval -- bar
|
||||
split:
|
||||
0 -- bar
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer '${FOO}'
|
||||
|
||||
check_output "var ref" <<EOF
|
||||
check_output_ws "var ref" <<EOF
|
||||
\${FOO}
|
||||
qrf ^----^
|
||||
eval -- bar
|
||||
split:
|
||||
0 -- bar
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer '\a'
|
||||
|
||||
check_output "escape" <<EOF
|
||||
check_output_ws "escape" <<EOF
|
||||
\a
|
||||
esc ^^
|
||||
eval -- a
|
||||
split:
|
||||
0 -- a
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer '\'
|
||||
|
||||
check_output "error" <<EOF
|
||||
check_output_ws "error" <<EOF
|
||||
\\
|
||||
err ^
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer "'abc'"
|
||||
|
||||
check_output "single" <<EOF
|
||||
check_output_ws "single" <<EOF
|
||||
'abc'
|
||||
sst ^
|
||||
sen ^
|
||||
eval -- abc
|
||||
split:
|
||||
0 -- abc
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer '"def"'
|
||||
|
||||
check_output "double" <<EOF
|
||||
check_output_ws "double" <<EOF
|
||||
"def"
|
||||
dst ^
|
||||
den ^
|
||||
eval -- def
|
||||
split:
|
||||
0 -- def
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer '"'"'"'"'
|
||||
|
||||
check_output "double with single" <<EOF
|
||||
check_output_ws "double with single" <<EOF
|
||||
"'"
|
||||
dst ^
|
||||
den ^
|
||||
eval -- '
|
||||
split:
|
||||
0 -- '
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer "'"'"'"'"
|
||||
|
||||
check_output "single with double" <<EOF
|
||||
check_output_ws "single with double" <<EOF
|
||||
'"'
|
||||
sst ^
|
||||
sen ^
|
||||
eval -- "
|
||||
split:
|
||||
0 -- "
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer '"abc $DEF 123"'
|
||||
|
||||
check_output "double w/ref" <<EOF
|
||||
check_output_ws "double w/ref" <<EOF
|
||||
"abc \$DEF 123"
|
||||
dst ^
|
||||
ref ^--^
|
||||
den ^
|
||||
eval -- abc xyz 123
|
||||
split:
|
||||
0 -- abc xyz 123
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer '"abc ${DEF} 123"'
|
||||
|
||||
check_output "double w/ref" <<EOF
|
||||
check_output_ws "double w/quoted-ref" <<EOF
|
||||
"abc \${DEF} 123"
|
||||
dst ^
|
||||
qrf ^----^
|
||||
den ^
|
||||
den ^
|
||||
eval -- abc xyz 123
|
||||
split:
|
||||
0 -- abc xyz 123
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer "'abc \$DEF 123'"
|
||||
|
||||
check_output "single w/ref" <<EOF
|
||||
check_output_ws "single w/ref" <<EOF
|
||||
'abc \$DEF 123'
|
||||
sst ^
|
||||
sen ^
|
||||
eval -- abc \$DEF 123
|
||||
split:
|
||||
0 -- abc \$DEF 123
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer 'abc $DEF 123'
|
||||
run_test ./drive_shlexer 'abc $DEF 123'
|
||||
|
||||
check_output "unquoted" <<EOF
|
||||
abc \$DEF 123
|
||||
check_output_ws "unquoted" <<EOF
|
||||
abc \$DEF 123
|
||||
wsp ^
|
||||
ref ^--^
|
||||
eval -- abc xyz 123
|
||||
wsp ^^
|
||||
eval -- abc xyz 123
|
||||
split:
|
||||
0 -- abc
|
||||
1 -- xyz
|
||||
2 -- 123
|
||||
EOF
|
||||
|
||||
run_test ./drive_shlexer '~ foo'
|
||||
|
||||
check_output_ws "tilde" <<EOF
|
||||
~ foo
|
||||
til ^
|
||||
wsp ^
|
||||
eval -- ../test foo
|
||||
split:
|
||||
0 -- ../test
|
||||
1 -- foo
|
||||
EOF
|
||||
|
@ -1,4 +1,4 @@
|
||||
|
||||
:eval :echo toplevel here $1 $2
|
||||
|
||||
|nested.lnav abc
|
||||
|nested.lnav abc $3
|
||||
|
Loading…
Reference in New Issue
Block a user