[cmd] replace wordexp with shlex

This commit is contained in:
Timothy Stack 2015-12-25 12:03:44 -08:00
parent 16e988d132
commit ab62d27757
10 changed files with 218 additions and 49 deletions

View File

@ -91,6 +91,17 @@ check_output() {
test_num=`expr ${test_num} \+ 1` 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() { test_err_filename() {
echo ${test_file_base}_${test_num}.err echo ${test_file_base}_${test_num}.err
} }

View File

@ -37,6 +37,7 @@
#include "pcrecpp.h" #include "pcrecpp.h"
#include "lnav.hh" #include "lnav.hh"
#include "log_format_loader.hh" #include "log_format_loader.hh"
#include "shlex.hh"
#include "command_executor.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> > scripts;
map<string, vector<string> >::iterator iter; map<string, vector<string> >::iterator iter;
static_root_mem<wordexp_t, wordfree> wordmem; vector<string> split_args;
string msg, retval; string msg, retval;
shlex lexer(path_and_args);
log_info("Executing file: %s", path_and_args.c_str()); log_info("Executing file: %s", path_and_args.c_str());
int exp_rc = wordexp(path_and_args.c_str(), if (!lexer.split(split_args, lnav_data.ld_local_vars.top())) {
wordmem.inout(), retval = "error: unable to parse path";
WRDE_NOCMD | WRDE_UNDEF);
if (!wordexperr(exp_rc, msg)) {
retval = msg;
} }
else if (wordmem->we_wordc == 0) { else if (split_args.empty()) {
retval = "error: no script specified"; retval = "error: no script specified";
} }
else { else {
lnav_data.ld_local_vars.push(map<string, string>()); 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(); map<string, string> &vars = lnav_data.ld_local_vars.top();
char env_arg_name[32]; char env_arg_name[32];
string result, open_error = "file not found"; 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; 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); 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; vector<string> paths_to_exec;

View File

@ -1925,7 +1925,7 @@ static void looper(void)
"to switch to the next/previous file")); "to switch to the next/previous file"));
} }
if (lnav_data.ld_view_stack.top() == &lnav_data.ld_views[LNV_TEXT] && 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) { lnav_data.ld_log_source.text_line_count() > 0) {
textview_curses *tc_log = &lnav_data.ld_views[LNV_LOG]; textview_curses *tc_log = &lnav_data.ld_views[LNV_LOG];
lnav_data.ld_view_stack.pop(); lnav_data.ld_view_stack.pop();

View File

@ -29,7 +29,6 @@
#include "config.h" #include "config.h"
#include <wordexp.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <string> #include <string>
@ -459,15 +458,13 @@ static string com_save_to(string cmdline, vector<string> &args)
fn = trim(remaining_args(cmdline, 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 (!lexer.split(split_args, lnav_data.ld_local_vars.top())) {
return "error: unable to parse arguments";
if (!wordexperr(rc, retval)) {
return retval;
} }
if (split_args.size() > 1) {
if (wordmem->we_wordc > 1) {
return "error: more than one file name was matched"; 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; outfile = stdout;
if (lnav_data.ld_flags & LNF_HEADLESS) { if (lnav_data.ld_flags & LNF_HEADLESS) {
lnav_data.ld_stdout_used = true; lnav_data.ld_stdout_used = true;
@ -515,8 +512,8 @@ static string com_save_to(string cmdline, vector<string> &args)
args[0].c_str()); args[0].c_str());
} }
} }
else if ((outfile = fopen(wordmem->we_wordv[0], mode)) == NULL) { else if ((outfile = fopen(split_args[0].c_str(), mode)) == NULL) {
return "error: unable to open file -- " + string(wordmem->we_wordv[0]); return "error: unable to open file -- " + split_args[0];
} }
if (args[0] == "write-csv-to") { if (args[0] == "write-csv-to") {
@ -1306,7 +1303,7 @@ static string com_open(string cmdline, vector<string> &args)
return retval; return retval;
} }
static_root_mem<wordexp_t, wordfree> wordmem; vector<string> word_exp;
list<logfile *>::iterator file_iter; list<logfile *>::iterator file_iter;
size_t colon_index; size_t colon_index;
int top = 0; int top = 0;
@ -1314,14 +1311,15 @@ static string com_open(string cmdline, vector<string> &args)
pat = trim(remaining_args(cmdline, 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)) { if (!lexer.split(split_args, lnav_data.ld_local_vars.top())) {
return retval; return "error: unable to parse arguments";
} }
for (size_t lpc = 0; lpc < wordmem->we_wordc; lpc++) { for (size_t lpc = 0; lpc < split_args.size(); lpc++) {
string fn = wordmem->we_wordv[lpc]; string fn = split_args[lpc];
if (startswith(fn, "pt:")) { if (startswith(fn, "pt:")) {
lnav_data.ld_pt_search = fn; lnav_data.ld_pt_search = fn;

View File

@ -516,6 +516,7 @@ void readline_shlex_highlighter(attr_line_t &al, int x)
&view_curses::VC_STYLE, &view_curses::VC_STYLE,
error_attrs)); error_attrs));
break; break;
case ST_TILDE:
case ST_ESCAPE: case ST_ESCAPE:
al.with_attr(string_attr( al.with_attr(string_attr(
line_range(cap.c_begin, cap.c_end), line_range(cap.c_begin, cap.c_end),
@ -556,6 +557,8 @@ void readline_shlex_highlighter(attr_line_t &al, int x)
} }
break; break;
} }
case ST_WHITESPACE:
break;
} }
} }

View File

@ -38,6 +38,7 @@
enum shlex_token_t { enum shlex_token_t {
ST_ERROR, ST_ERROR,
ST_WHITESPACE,
ST_ESCAPE, ST_ESCAPE,
ST_DOUBLE_QUOTE_START, ST_DOUBLE_QUOTE_START,
ST_DOUBLE_QUOTE_END, ST_DOUBLE_QUOTE_END,
@ -45,6 +46,7 @@ enum shlex_token_t {
ST_SINGLE_QUOTE_END, ST_SINGLE_QUOTE_END,
ST_VARIABLE_REF, ST_VARIABLE_REF,
ST_QUOTED_VARIABLE_REF, ST_QUOTED_VARIABLE_REF,
ST_TILDE,
}; };
class shlex { class shlex {
@ -126,6 +128,33 @@ public:
break; break;
} }
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: default:
break; break;
} }
@ -151,6 +180,9 @@ public:
case ST_ESCAPE: case ST_ESCAPE:
result.append(1, this->s_str[cap.c_begin + 1]); result.append(1, this->s_str[cap.c_begin + 1]);
break; break;
case ST_WHITESPACE:
result.append(&this->s_str[cap.c_begin], cap.length());
break;
case ST_VARIABLE_REF: case ST_VARIABLE_REF:
case ST_QUOTED_VARIABLE_REF: { case ST_QUOTED_VARIABLE_REF: {
int extra = token == ST_VARIABLE_REF ? 0 : 1; int extra = token == ST_VARIABLE_REF ? 0 : 1;
@ -166,6 +198,17 @@ public:
} }
break; break;
} }
case ST_TILDE: {
const char *home_dir = getenv("HOME");
if (home_dir != NULL) {
result.append(home_dir);
}
else {
result.append("~");
}
break;
}
default: default:
break; break;
} }
@ -177,6 +220,74 @@ public:
return true; 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() { void reset() {
this->s_index = 0; this->s_index = 0;
this->s_state = STATE_NORMAL; this->s_state = STATE_NORMAL;

View File

@ -37,6 +37,7 @@ using namespace std;
const char *ST_TOKEN_NAMES[] = { const char *ST_TOKEN_NAMES[] = {
"err", "err",
"wsp",
"esc", "esc",
"dst", "dst",
"den", "den",
@ -44,6 +45,7 @@ const char *ST_TOKEN_NAMES[] = {
"sen", "sen",
"ref", "ref",
"qrf", "qrf",
"til",
}; };
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -84,6 +86,14 @@ int main(int argc, char *argv[])
if (lexer.eval(result, map<string, string>())) { if (lexer.eval(result, map<string, string>())) {
printf("eval -- %s\n", result.c_str()); 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; return EXIT_SUCCESS;
} }

View File

@ -1,7 +1,7 @@
#! /bin/bash #! /bin/bash
run_test ${lnav_test} -n \ 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 ${test_dir}/logfile_access_log.0
check_error_output "include toplevel.lnav" <<EOF check_error_output "include toplevel.lnav" <<EOF
@ -9,7 +9,7 @@ EOF
check_output "include toplevel.lnav" <<EOF check_output "include toplevel.lnav" <<EOF
toplevel here 123 456 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: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/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" 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"

View File

@ -5,104 +5,142 @@ export DEF='xyz'
run_test ./drive_shlexer '$FOO' run_test ./drive_shlexer '$FOO'
check_output "var ref" <<EOF check_output_ws "var ref" <<EOF
\$FOO \$FOO
ref ^--^ ref ^--^
eval -- bar eval -- bar
split:
0 -- bar
EOF EOF
run_test ./drive_shlexer '${FOO}' run_test ./drive_shlexer '${FOO}'
check_output "var ref" <<EOF check_output_ws "var ref" <<EOF
\${FOO} \${FOO}
qrf ^----^ qrf ^----^
eval -- bar eval -- bar
split:
0 -- bar
EOF EOF
run_test ./drive_shlexer '\a' run_test ./drive_shlexer '\a'
check_output "escape" <<EOF check_output_ws "escape" <<EOF
\a \a
esc ^^ esc ^^
eval -- a eval -- a
split:
0 -- a
EOF EOF
run_test ./drive_shlexer '\' run_test ./drive_shlexer '\'
check_output "error" <<EOF check_output_ws "error" <<EOF
\\ \\
err ^ err ^
EOF EOF
run_test ./drive_shlexer "'abc'" run_test ./drive_shlexer "'abc'"
check_output "single" <<EOF check_output_ws "single" <<EOF
'abc' 'abc'
sst ^ sst ^
sen ^ sen ^
eval -- abc eval -- abc
split:
0 -- abc
EOF EOF
run_test ./drive_shlexer '"def"' run_test ./drive_shlexer '"def"'
check_output "double" <<EOF check_output_ws "double" <<EOF
"def" "def"
dst ^ dst ^
den ^ den ^
eval -- def eval -- def
split:
0 -- def
EOF EOF
run_test ./drive_shlexer '"'"'"'"' run_test ./drive_shlexer '"'"'"'"'
check_output "double with single" <<EOF check_output_ws "double with single" <<EOF
"'" "'"
dst ^ dst ^
den ^ den ^
eval -- ' eval -- '
split:
0 -- '
EOF EOF
run_test ./drive_shlexer "'"'"'"'" run_test ./drive_shlexer "'"'"'"'"
check_output "single with double" <<EOF check_output_ws "single with double" <<EOF
'"' '"'
sst ^ sst ^
sen ^ sen ^
eval -- " eval -- "
split:
0 -- "
EOF EOF
run_test ./drive_shlexer '"abc $DEF 123"' run_test ./drive_shlexer '"abc $DEF 123"'
check_output "double w/ref" <<EOF check_output_ws "double w/ref" <<EOF
"abc \$DEF 123" "abc \$DEF 123"
dst ^ dst ^
ref ^--^ ref ^--^
den ^ den ^
eval -- abc xyz 123 eval -- abc xyz 123
split:
0 -- abc xyz 123
EOF EOF
run_test ./drive_shlexer '"abc ${DEF} 123"' run_test ./drive_shlexer '"abc ${DEF} 123"'
check_output "double w/ref" <<EOF check_output_ws "double w/quoted-ref" <<EOF
"abc \${DEF} 123" "abc \${DEF} 123"
dst ^ dst ^
qrf ^----^ qrf ^----^
den ^ den ^
eval -- abc xyz 123 eval -- abc xyz 123
split:
0 -- abc xyz 123
EOF EOF
run_test ./drive_shlexer "'abc \$DEF 123'" run_test ./drive_shlexer "'abc \$DEF 123'"
check_output "single w/ref" <<EOF check_output_ws "single w/ref" <<EOF
'abc \$DEF 123' 'abc \$DEF 123'
sst ^ sst ^
sen ^ sen ^
eval -- abc \$DEF 123 eval -- abc \$DEF 123
split:
0 -- abc \$DEF 123
EOF EOF
run_test ./drive_shlexer 'abc $DEF 123' run_test ./drive_shlexer 'abc $DEF 123'
check_output "unquoted" <<EOF check_output_ws "unquoted" <<EOF
abc \$DEF 123 abc \$DEF 123
wsp ^
ref ^--^ 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 EOF

View File

@ -1,4 +1,4 @@
:eval :echo toplevel here $1 $2 :eval :echo toplevel here $1 $2
|nested.lnav abc |nested.lnav abc $3