Commit 52a0b767c8b2acb18bbdc076b258092dc122a1c6

Authored by Jay Berkenbilt
1 parent 6b90f3db

Slightly improve bash completion arg parsing

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