Commit 52a0b767c8b2acb18bbdc076b258092dc122a1c6
1 parent
6b90f3db
Slightly improve bash completion arg parsing
Showing
4 changed files
with
76 additions
and
13 deletions
qpdf/qpdf.cc
| @@ -1414,30 +1414,85 @@ void | @@ -1414,30 +1414,85 @@ void | ||
| 1414 | ArgParser::handleBashArguments() | 1414 | ArgParser::handleBashArguments() |
| 1415 | { | 1415 | { |
| 1416 | // Do a minimal job of parsing bash_line into arguments. This | 1416 | // Do a minimal job of parsing bash_line into arguments. This |
| 1417 | - // doesn't do everything the shell does, but it should be good | ||
| 1418 | - // enough for purposes of handling completion. We can't use | ||
| 1419 | - // new_argv because this has to interoperate with @file arguments. | ||
| 1420 | - | ||
| 1421 | - enum { st_top, st_quote } state = st_top; | 1417 | + // doesn't do everything the shell does (e.g. $(...), variable |
| 1418 | + // expansion, arithmetic, globs, etc.), but it should be good | ||
| 1419 | + // enough for purposes of handling completion. As we build up the | ||
| 1420 | + // new argv, we can't use this->new_argv because this code has to | ||
| 1421 | + // interoperate with @file arguments, so memory for both ways of | ||
| 1422 | + // fabricating argv has to be protected. | ||
| 1423 | + | ||
| 1424 | + bool last_was_backslash = false; | ||
| 1425 | + enum { st_top, st_squote, st_dquote } state = st_top; | ||
| 1422 | std::string arg; | 1426 | std::string arg; |
| 1423 | for (std::string::iterator iter = bash_line.begin(); | 1427 | for (std::string::iterator iter = bash_line.begin(); |
| 1424 | iter != bash_line.end(); ++iter) | 1428 | iter != bash_line.end(); ++iter) |
| 1425 | { | 1429 | { |
| 1426 | char ch = (*iter); | 1430 | char ch = (*iter); |
| 1427 | - if ((state == st_top) && QUtil::is_space(ch) && (! arg.empty())) | 1431 | + if (last_was_backslash) |
| 1428 | { | 1432 | { |
| 1429 | - bash_argv.push_back( | ||
| 1430 | - PointerHolder<char>( | ||
| 1431 | - true, QUtil::copy_string(arg.c_str()))); | ||
| 1432 | - arg.clear(); | 1433 | + arg.append(1, ch); |
| 1434 | + last_was_backslash = false; | ||
| 1435 | + } | ||
| 1436 | + else if (ch == '\\') | ||
| 1437 | + { | ||
| 1438 | + last_was_backslash = true; | ||
| 1433 | } | 1439 | } |
| 1434 | else | 1440 | else |
| 1435 | { | 1441 | { |
| 1436 | - if (ch == '"') | 1442 | + bool append = false; |
| 1443 | + switch (state) | ||
| 1437 | { | 1444 | { |
| 1438 | - state = (state == st_top ? st_quote : st_top); | 1445 | + case st_top: |
| 1446 | + if (QUtil::is_space(ch)) | ||
| 1447 | + { | ||
| 1448 | + if (! arg.empty()) | ||
| 1449 | + { | ||
| 1450 | + bash_argv.push_back( | ||
| 1451 | + PointerHolder<char>( | ||
| 1452 | + true, QUtil::copy_string(arg.c_str()))); | ||
| 1453 | + arg.clear(); | ||
| 1454 | + } | ||
| 1455 | + } | ||
| 1456 | + else if (ch == '"') | ||
| 1457 | + { | ||
| 1458 | + state = st_dquote; | ||
| 1459 | + } | ||
| 1460 | + else if (ch == '\'') | ||
| 1461 | + { | ||
| 1462 | + state = st_squote; | ||
| 1463 | + } | ||
| 1464 | + else | ||
| 1465 | + { | ||
| 1466 | + append = true; | ||
| 1467 | + } | ||
| 1468 | + break; | ||
| 1469 | + | ||
| 1470 | + case st_squote: | ||
| 1471 | + if (ch == '\'') | ||
| 1472 | + { | ||
| 1473 | + state = st_top; | ||
| 1474 | + } | ||
| 1475 | + else | ||
| 1476 | + { | ||
| 1477 | + append = true; | ||
| 1478 | + } | ||
| 1479 | + break; | ||
| 1480 | + | ||
| 1481 | + case st_dquote: | ||
| 1482 | + if (ch == '"') | ||
| 1483 | + { | ||
| 1484 | + state = st_top; | ||
| 1485 | + } | ||
| 1486 | + else | ||
| 1487 | + { | ||
| 1488 | + append = true; | ||
| 1489 | + } | ||
| 1490 | + break; | ||
| 1491 | + } | ||
| 1492 | + if (append) | ||
| 1493 | + { | ||
| 1494 | + arg.append(1, ch); | ||
| 1439 | } | 1495 | } |
| 1440 | - arg.append(1, ch); | ||
| 1441 | } | 1496 | } |
| 1442 | } | 1497 | } |
| 1443 | if (bash_argv.empty()) | 1498 | if (bash_argv.empty()) |
qpdf/qtest/qpdf.test
| @@ -117,6 +117,12 @@ my @completion_tests = ( | @@ -117,6 +117,12 @@ my @completion_tests = ( | ||
| 117 | ['qpdf --decode-lzzz', 15, 'decode-l'], | 117 | ['qpdf --decode-lzzz', 15, 'decode-l'], |
| 118 | ['qpdf --decode-level=', undef, 'decode-level'], | 118 | ['qpdf --decode-level=', undef, 'decode-level'], |
| 119 | ['qpdf --check -', undef, 'later-arg'], | 119 | ['qpdf --check -', undef, 'later-arg'], |
| 120 | + ['qpdf infile outfile oops --ch', undef, 'usage-empty'], | ||
| 121 | + ['qpdf --encrypt \'user " password\' ', undef, 'quoting'], | ||
| 122 | + ['qpdf --encrypt \'user password\' ', undef, 'quoting'], | ||
| 123 | + ['qpdf --encrypt "user password" ', undef, 'quoting'], | ||
| 124 | + ['qpdf --encrypt "user pass\'word" ', undef, 'quoting'], | ||
| 125 | + ['qpdf --encrypt user\ password ', undef, 'quoting'], | ||
| 120 | ); | 126 | ); |
| 121 | $n_tests += scalar(@completion_tests); | 127 | $n_tests += scalar(@completion_tests); |
| 122 | foreach my $c (@completion_tests) | 128 | foreach my $c (@completion_tests) |
qpdf/qtest/qpdf/completion-quoting.out
0 → 100644
| 1 | +owner-password |
qpdf/qtest/qpdf/completion-usage-empty.out
0 → 100644
| 1 | +!--check |