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 | 1414 | ArgParser::handleBashArguments() |
| 1415 | 1415 | { |
| 1416 | 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 | 1426 | std::string arg; |
| 1423 | 1427 | for (std::string::iterator iter = bash_line.begin(); |
| 1424 | 1428 | iter != bash_line.end(); ++iter) |
| 1425 | 1429 | { |
| 1426 | 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 | 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 | 1498 | if (bash_argv.empty()) | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -117,6 +117,12 @@ my @completion_tests = ( |
| 117 | 117 | ['qpdf --decode-lzzz', 15, 'decode-l'], |
| 118 | 118 | ['qpdf --decode-level=', undef, 'decode-level'], |
| 119 | 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 | 127 | $n_tests += scalar(@completion_tests); |
| 122 | 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 | ... | ... |