summaryrefslogtreecommitdiffstats
path: root/src/util.cc
diff options
context:
space:
mode:
authorNicholas Hutchinson <nshutchinson@gmail.com>2013-11-30 22:33:02 (GMT)
committerNicholas Hutchinson <nshutchinson@gmail.com>2014-01-06 19:30:28 (GMT)
commit664edba804ddd5363299f2af5183109bdc9715b1 (patch)
treec50e267ade56530904676f103905d4a8a2ba3ba0 /src/util.cc
parent2517b75781decd49bc82f05f8336c509f7b2c62e (diff)
downloadNinja-664edba804ddd5363299f2af5183109bdc9715b1.zip
Ninja-664edba804ddd5363299f2af5183109bdc9715b1.tar.gz
Ninja-664edba804ddd5363299f2af5183109bdc9715b1.tar.bz2
More robust escaping of $in, $out paths
In summary: don’t escape if the path doesn’t contain problematic characters, otherwise: - Shell: enclose string in single quotes, escape embedded single quotes with the magic quote-backslash-quote sequence - Win32: Escape double quotes by doubling the number of consecutive backslashes that precede them (if any) and adding one more. Finally, double the number of trailing backslashes, and enclose the whole thing in double quotes.
Diffstat (limited to 'src/util.cc')
-rw-r--r--src/util.cc104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/util.cc b/src/util.cc
index 6ba3c6c..0e4dc59 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -20,6 +20,7 @@
#include <share.h>
#endif
+#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
@@ -175,6 +176,109 @@ bool CanonicalizePath(char* path, size_t* len, string* err) {
return true;
}
+static inline bool IsKnownShellSafeCharacter(char ch) {
+ if ('A' <= ch && ch <= 'Z') return true;
+ if ('a' <= ch && ch <= 'z') return true;
+ if ('0' <= ch && ch <= '9') return true;
+
+ switch (ch) {
+ case '_':
+ case '-':
+ case '.':
+ case '/':
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool IsKnownWin32SafeCharacter(char ch) {
+ switch (ch) {
+ case '\\':
+ case ' ':
+ case '"':
+ return false;
+ default:
+ return true;
+ }
+}
+
+static inline bool StringNeedsShellEscaping(const string& input) {
+ for (size_t i = 0; i < input.size(); ++i) {
+ if (!IsKnownShellSafeCharacter(input[i])) return true;
+ }
+ return false;
+}
+
+static inline bool StringNeedsWin32Escaping(const string& input) {
+ for (size_t i = 0; i < input.size(); ++i) {
+ if (!IsKnownWin32SafeCharacter(input[i])) return true;
+ }
+ return false;
+}
+
+void GetShellEscapedString(const string& input, string* result) {
+ assert(result);
+
+ if (!StringNeedsShellEscaping(input)) {
+ result->append(input);
+ return;
+ }
+
+ const char kQuote = '\'';
+ const char kEscapeSequence[] = "'\\'";
+
+ result->push_back(kQuote);
+
+ string::const_iterator span_begin = input.begin();
+ for (string::const_iterator it = input.begin(), end = input.end(); it != end;
+ ++it) {
+ if (*it == kQuote) {
+ result->append(span_begin, it);
+ result->append(kEscapeSequence);
+ span_begin = it;
+ }
+ }
+ result->append(span_begin, input.end());
+ result->push_back(kQuote);
+}
+
+
+void GetWin32EscapedString(const string& input, string* result) {
+ assert(result);
+ if (!StringNeedsWin32Escaping(input)) {
+ result->append(input);
+ return;
+ }
+
+ const char kQuote = '"';
+ const char kBackslash = '\\';
+
+ result->push_back(kQuote);
+ size_t consecutive_backslash_count = 0;
+ string::const_iterator span_begin = input.begin();
+ for (string::const_iterator it = input.begin(), end = input.end(); it != end;
+ ++it) {
+ switch (*it) {
+ case kBackslash:
+ ++consecutive_backslash_count;
+ break;
+ case kQuote:
+ result->append(span_begin, it);
+ result->append(consecutive_backslash_count + 1, kBackslash);
+ span_begin = it;
+ consecutive_backslash_count = 0;
+ break;
+ default:
+ consecutive_backslash_count = 0;
+ break;
+ }
+ }
+ result->append(span_begin, input.end());
+ result->append(consecutive_backslash_count, kBackslash);
+ result->push_back(kQuote);
+}
+
int ReadFile(const string& path, string* contents, string* err) {
FILE* f = fopen(path.c_str(), "r");
if (!f) {