Commit 52a0b767c8b2acb18bbdc076b258092dc122a1c6

Authored by Jay Berkenbilt
1 parent 6b90f3db

Slightly improve bash completion arg parsing

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
... ...