[cmd] replace wordexp with shlex

pull/290/head
Timothy Stack 9 years ago
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;
}
}

@ -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…
Cancel
Save