| // Copyright 2012 The Goma Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| |
| #include "lib/cmdline_parser.h" |
| |
| #include <ctype.h> |
| |
| #include "absl/base/macros.h" |
| #include "glog/logging.h" |
| #include "glog/stl_logging.h" |
| using std::string; |
| |
| namespace devtools_goma { |
| |
| // Parsing Command-Line Arguments (on posix for gcc, javac) |
| bool ParsePosixCommandLineToArgv(absl::string_view cmdline, |
| std::vector<string>* argv) { |
| bool dquote = false; |
| bool squote = false; |
| bool backslash = false; |
| bool in_arg = false; |
| string arg = ""; |
| |
| for (size_t i = 0; i < cmdline.size(); ++i) { |
| char ch = cmdline[i]; |
| if (!in_arg) { |
| if (isspace(ch)) continue; |
| in_arg = true; |
| } |
| DCHECK(in_arg); |
| if (isspace(ch) && !squote && !dquote && !backslash) { |
| in_arg = false; |
| argv->push_back(arg); |
| arg = ""; |
| continue; |
| } |
| if (squote) { // in single quote, anything will be saved as-is. |
| if (ch == '\'') { |
| squote = false; |
| continue; |
| } |
| arg += ch; |
| continue; |
| } |
| DCHECK(!squote); |
| if (backslash) { |
| backslash = false; |
| if (ch == '\n') |
| continue; |
| // "a\b" -> a\b, "a\\b" -> a\b, "a\"b" -> a"b, "a\bc" -> abc |
| if (dquote && ch != '\\' && ch != '"') |
| arg += '\\'; |
| arg += ch; |
| continue; |
| } |
| DCHECK(!backslash); |
| if (ch == '\\') { // backslash is available inside quote. |
| backslash = true; |
| continue; |
| } |
| if (dquote) { |
| if (ch == '\"') { |
| dquote = false; |
| continue; |
| } |
| arg += ch; |
| continue; |
| } |
| DCHECK(!dquote); |
| if (ch == '\'') { |
| squote = true; |
| continue; |
| } |
| if (ch == '\"') { |
| dquote = true; |
| continue; |
| } |
| arg += ch; |
| } |
| if (in_arg) { |
| argv->push_back(arg); |
| } |
| if (backslash) { |
| LOG(ERROR) << "no next char for backslash: " << cmdline; |
| return false; |
| } |
| if (squote || dquote) { |
| LOG(ERROR) << "no closing quote: " << cmdline; |
| return false; |
| } |
| return true; |
| } |
| |
| // Parsing Command-Line Arguments (on Windows) |
| // http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft(v=vs.85).aspx |
| bool ParseWinCommandLineToArgv(absl::string_view cmdline, |
| std::vector<string>* argv) { |
| size_t num_backslash = 0; |
| bool arg_delimiter = false; |
| bool in_quote = false; |
| string arg = ""; |
| for (size_t i = 0; i < cmdline.size(); ++i) { |
| char c = cmdline[i]; |
| switch (c) { |
| case '\\': |
| ++num_backslash; |
| continue; |
| case '"': |
| if (num_backslash > 0) { |
| // If an even number of backslashes is followed by a double |
| // quotation mark, one backslash is placed in the argv array for |
| // every pair of backslashes, and the double quotation mark is |
| // interpreted as a string delimiter. |
| for (size_t j = 0; j < num_backslash / 2; ++j) { |
| arg += "\\"; |
| } |
| // If an odd number of backslashes is followed by a double quotation |
| // mark, one backslash is placed in the argv array for every pair of |
| // backslashes, and the double quotation mark is "escaped" by the |
| // remaining backslash, causing a literal double quotation mark (") |
| // to be placed in argv |
| if (num_backslash % 2 == 1) { |
| arg += "\""; |
| } else { |
| in_quote = !in_quote; |
| } |
| } else { |
| in_quote = !in_quote; |
| } |
| num_backslash = 0; |
| continue; |
| case ' ': case '\t': case '\r': case '\n': |
| if (!in_quote) |
| arg_delimiter = true; |
| ABSL_FALLTHROUGH_INTENDED; |
| default: |
| // Backslashes are interpreted literally, unless they immediately |
| // precede a double quotation mark. |
| if (num_backslash > 0) { |
| for (size_t j = 0; j < num_backslash; ++j) |
| arg += "\\"; |
| num_backslash = 0; |
| } |
| if (arg_delimiter) { |
| // We cannot handle "" as an empty argument, but it might |
| // never be a problem. |
| if (!arg.empty()) |
| argv->push_back(arg); |
| arg = ""; |
| arg_delimiter = false; |
| } else { |
| arg += c; |
| } |
| } |
| } |
| // Last argument. |
| // Backslashes are interpreted literally, unless they immediately |
| // precede a double quotation mark. |
| if (num_backslash > 0) { |
| for (size_t j = 0; j < num_backslash; ++j) |
| arg += "\\"; |
| } |
| if (!arg.empty()) |
| argv->push_back(arg); |
| |
| if (in_quote) { |
| LOG(ERROR) << "no closing quote: " << cmdline; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace devtools_goma |