Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(287)

Side by Side Diff: chrome/install_static/install_util.cc

Issue 2543503003: Handle spaces and quotes in chrome_elf command line parser (Closed)
Patch Set: use wcs* Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/install_static/install_util.h" 5 #include "chrome/install_static/install_util.h"
6 6
7 #include <windows.h> 7 #include <windows.h>
8 #include <assert.h> 8 #include <assert.h>
9 #include <stdlib.h> 9 #include <stdlib.h>
10 #include <string.h> 10 #include <string.h>
(...skipping 563 matching lines...) Expand 10 before | Expand all | Expand 10 after
574 bool trim_spaces) { 574 bool trim_spaces) {
575 return TokenizeStringT<std::string>(str, delimiter, trim_spaces); 575 return TokenizeStringT<std::string>(str, delimiter, trim_spaces);
576 } 576 }
577 577
578 std::vector<std::wstring> TokenizeString16(const std::wstring& str, 578 std::vector<std::wstring> TokenizeString16(const std::wstring& str,
579 wchar_t delimiter, 579 wchar_t delimiter,
580 bool trim_spaces) { 580 bool trim_spaces) {
581 return TokenizeStringT<std::wstring>(str, delimiter, trim_spaces); 581 return TokenizeStringT<std::wstring>(str, delimiter, trim_spaces);
582 } 582 }
583 583
584 std::vector<std::wstring> TokenizeCommandLineToArray(
585 const std::wstring& command_line) {
586 // This is baroquely complex to do properly, see e.g.
587 // https://blogs.msdn.microsoft.com/oldnewthing/20100917-00/?p=12833
588 // http://www.windowsinspired.com/how-a-windows-programs-splits-its-command-li ne-into-individual-arguments/
589 // and many others. We cannot use CommandLineToArgvW() in chrome_elf, because
590 // it's in shell32.dll. Previously, __wgetmainargs() in the CRT was available,
591 // and it's still documented for VS 2015 at
592 // https://msdn.microsoft.com/en-us/library/ff770599.aspx but unfortunately,
593 // isn't actually available.
594 //
595 // This parsing matches CommandLineToArgvW()s for arguments, rather than the
596 // CRTs. These are different only in the most obscure of cases and will not
597 // matter in any practical situation. See the windowsinspired.com post above
598 // for details.
599 //
600 // TODO(scottmg): argv[0] normally has special handling. We should likely do
601 // that.
602
603 enum class SpecialChars {
grt (UTC plus 2) 2016/12/01 09:07:13 i think some comments in here are worthwhile. i've
scottmg 2016/12/01 19:04:21 Done.
604 kInterpret,
grt (UTC plus 2) 2016/12/01 09:07:13 // Space or tab, if encountered, delimit a break b
scottmg 2016/12/01 19:04:21 Done.
605 kIgnore,
grt (UTC plus 2) 2016/12/01 09:07:12 // Space or tab, if encountered, are part of the c
scottmg 2016/12/01 19:04:21 Done.
606 } state;
607
608 std::vector<std::wstring> result;
609 std::wstring token;
610
611 const wchar_t* p = command_line.c_str();
612 // This loops the entire string, with a subloop for each argument.
613 for (;;) {
614 // Advance past leading whitespace (only space and tab are handled).
615 p += wcsspn(p, L" \t");
616
617 // End of arguments.
618 if (p[0] == 0) {
619 if (!token.empty())
620 result.push_back(token);
621 break;
622 }
623
624 state = SpecialChars::kInterpret;
625
626 // Scan an argument.
627 for (;;) {
628 // Count and advance past leading backslashes.
grt (UTC plus 2) 2016/12/01 09:07:12 nit: since this counts all backslashes, not only l
scottmg 2016/12/01 19:04:21 Done.
629 int num_backslashes = 0;
630 while (p[0] == L'\\') {
631 ++p;
632 ++num_backslashes;
633 }
634
635 if (p[0] == L'"') {
636 for (int i = 1; i < num_backslashes; i += 2)
grt (UTC plus 2) 2016/12/01 09:07:13 tiny nit: a single append will be more performant
scottmg 2016/12/01 19:04:22 Done. (num_backslashes / 2) though.
grt (UTC plus 2) 2016/12/02 10:18:03 Oops. Cool.
637 token += L'\\';
638
639 if (num_backslashes % 2 == 1) {
640 token += L'"';
grt (UTC plus 2) 2016/12/01 09:07:13 // An odd number of backslashes followed by a quot
scottmg 2016/12/01 19:04:21 Done.
641 } else if (p[1] == L'"' && state == SpecialChars::kIgnore) {
grt (UTC plus 2) 2016/12/01 09:07:13 will this not be an OOB read for a string that end
scottmg 2016/12/01 19:04:21 I think it's safe, because .c_str() is null termin
642 ++p;
grt (UTC plus 2) 2016/12/01 09:07:12 // Special case for consecutive double quotes whil
scottmg 2016/12/01 19:04:22 Done.
643 token += L'"';
644 state = SpecialChars::kInterpret;
645 } else {
646 state = state == SpecialChars::kInterpret ? SpecialChars::kIgnore
647 : SpecialChars::kInterpret;
648 }
649 } else {
650 for (int i = 0; i < num_backslashes; ++i)
grt (UTC plus 2) 2016/12/01 09:07:13 // Emit backslashes that do not precede a double q
scottmg 2016/12/01 19:04:22 Done.
651 token += L'\\';
652 if (p[0] == 0 ||
653 (wcschr(L" \t", p[0]) && state == SpecialChars::kInterpret)) {
grt (UTC plus 2) 2016/12/01 09:07:12 nit: swap these two so that the cheap condition is
scottmg 2016/12/01 19:04:21 Done.
654 result.push_back(token);
655 token.clear();
656 break;
657 } else {
grt (UTC plus 2) 2016/12/01 09:07:12 nit: omit "else" since the "if" branch breaks for
scottmg 2016/12/01 19:04:21 Done.
658 token += *p;
659 }
660 }
661
662 ++p;
663 }
664 }
665
666 return result;
667 }
668
584 std::wstring GetSwitchValueFromCommandLine(const std::wstring& command_line, 669 std::wstring GetSwitchValueFromCommandLine(const std::wstring& command_line,
585 const std::wstring& switch_name) { 670 const std::wstring& switch_name) {
586 assert(!command_line.empty()); 671 assert(!command_line.empty());
587 assert(!switch_name.empty()); 672 assert(!switch_name.empty());
588 673
589 std::wstring command_line_copy = command_line; 674 std::vector<std::wstring> as_array = TokenizeCommandLineToArray(command_line);
590 // Remove leading and trailing spaces. 675 std::wstring switch_with_equal = L"--" + switch_name + L"=";
591 TrimT<std::wstring>(&command_line_copy); 676 for (size_t i = 1; i < as_array.size(); ++i) {
grt (UTC plus 2) 2016/12/01 09:07:12 nit for (const std::wstring& arg : as_array) {
scottmg 2016/12/01 19:04:22 I'd prefer that, but is there an easy way to do th
grt (UTC plus 2) 2016/12/02 10:18:03 oh, 1, duh. apologies
677 const std::wstring& arg = as_array[i];
678 if (arg.compare(0, switch_with_equal.size(), switch_with_equal) == 0) {
grt (UTC plus 2) 2016/12/01 09:07:13 nit: omit braces
scottmg 2016/12/01 19:04:22 Heh, done. I forgot to switch from mark@ back to g
679 return arg.substr(switch_with_equal.size());
680 }
681 }
592 682
593 // Find the switch in the command line. If we don't find the switch, return 683 return std::wstring();
594 // an empty string.
595 std::wstring switch_token = L"--";
596 switch_token += switch_name;
597 switch_token += L"=";
598 size_t switch_offset = command_line_copy.find(switch_token);
599 if (switch_offset == std::string::npos)
600 return std::wstring();
601
602 // The format is "--<switch name>=blah". Look for a space after the
603 // "--<switch name>=" string. If we don't find a space assume that the switch
604 // value ends at the end of the command line.
605 size_t switch_value_start_offset = switch_offset + switch_token.length();
606 if (std::wstring(kWhiteSpaces16).find(
607 command_line_copy[switch_value_start_offset]) != std::wstring::npos) {
608 switch_value_start_offset = command_line_copy.find_first_not_of(
609 GetWhiteSpacesForType<std::wstring>(), switch_value_start_offset);
610 if (switch_value_start_offset == std::wstring::npos)
611 return std::wstring();
612 }
613 size_t switch_value_end_offset =
614 command_line_copy.find_first_of(GetWhiteSpacesForType<std::wstring>(),
615 switch_value_start_offset);
616 if (switch_value_end_offset == std::wstring::npos)
617 switch_value_end_offset = command_line_copy.length();
618
619 std::wstring switch_value = command_line_copy.substr(
620 switch_value_start_offset,
621 switch_value_end_offset - (switch_offset + switch_token.length()));
622 TrimT<std::wstring>(&switch_value);
623 return switch_value;
624 } 684 }
625 685
626 bool RecursiveDirectoryCreate(const std::wstring& full_path) { 686 bool RecursiveDirectoryCreate(const std::wstring& full_path) {
627 // If the path exists, we've succeeded if it's a directory, failed otherwise. 687 // If the path exists, we've succeeded if it's a directory, failed otherwise.
628 const wchar_t* full_path_str = full_path.c_str(); 688 const wchar_t* full_path_str = full_path.c_str();
629 DWORD file_attributes = ::GetFileAttributes(full_path_str); 689 DWORD file_attributes = ::GetFileAttributes(full_path_str);
630 if (file_attributes != INVALID_FILE_ATTRIBUTES) { 690 if (file_attributes != INVALID_FILE_ATTRIBUTES) {
631 if ((file_attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { 691 if ((file_attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
632 Trace(L"%hs( %ls directory exists )\n", __func__, full_path_str); 692 Trace(L"%hs( %ls directory exists )\n", __func__, full_path_str);
633 return true; 693 return true;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
683 case ChannelStrategy::ADDITIONAL_PARAMETERS: 743 case ChannelStrategy::ADDITIONAL_PARAMETERS:
684 return ChannelFromAdditionalParameters(mode, system_level, multi_install); 744 return ChannelFromAdditionalParameters(mode, system_level, multi_install);
685 case ChannelStrategy::FIXED: 745 case ChannelStrategy::FIXED:
686 return mode.default_channel_name; 746 return mode.default_channel_name;
687 } 747 }
688 748
689 return std::wstring(); 749 return std::wstring();
690 } 750 }
691 751
692 } // namespace install_static 752 } // namespace install_static
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698