aboutsummaryrefslogtreecommitdiff
path: root/build2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-07-23 16:09:49 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-07-25 08:05:43 +0200
commitad7d4bd0722aa70ba634900cebf93a1b1814fed9 (patch)
treefbfa28c4602dea0be178e63d1e6a1b2667e85190 /build2
parenta8bef4ff20100c518816c641ae1ff9783306c167 (diff)
Only do "effective escaping" (['"\$(]) on the command line
This will make things more convenient on Windows provided we use "sane" paths (no spaces, no (), etc).
Diffstat (limited to 'build2')
-rw-r--r--build2/b.cli15
-rw-r--r--build2/b.cxx2
-rw-r--r--build2/context.cxx9
-rw-r--r--build2/lexer16
-rw-r--r--build2/lexer.cxx34
-rw-r--r--build2/parser.cxx5
6 files changed, 57 insertions, 24 deletions
diff --git a/build2/b.cli b/build2/b.cli
index 013a756..f0cd48e 100644
--- a/build2/b.cli
+++ b/build2/b.cli
@@ -178,6 +178,21 @@ namespace build2
config.install.root=/usr/local config.install.root.sudo=sudo \
configure
\
+
+ Note also that buildspec and command line variable values are treated as
+ \cb{buildfile} fragments and so can use quoting and escaping as well as
+ contain variable expansions and evaluation contexts. However, to be more
+ usable on various platforms, escaping in these two situations is limited
+ to the \i{effective sequences} of \cb{\\'}, \cb{\\\"}, \cb{\\\\},
+ \cb{\\$}, and \cb{\\(} with all other sequences interpreted as is.
+ Together with double-quoting this is sufficient to represent any value.
+ For example:
+
+ \
+ b config.install.root=c:\projects\install
+ b \"config.install.root='c:\Program Files (x86)\test\'\"
+ b 'config.cxx.poptions=-DFOO_STR=\"foo\"'
+ \
"
}
diff --git a/build2/b.cxx b/build2/b.cxx
index 748eda7..b2ba2c1 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -30,8 +30,6 @@
#include <build2/context>
#include <build2/variable>
-#include <build2/token>
-#include <build2/lexer>
#include <build2/parser>
#include <build2/b-options>
diff --git a/build2/context.cxx b/build2/context.cxx
index e08fb52..f756bfc 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -97,7 +97,12 @@ namespace build2
{
istringstream is (s);
is.exceptions (istringstream::failbit | istringstream::badbit);
- lexer l (is, path ("<cmdline>"));
+
+ // Similar to buildspec we do "effective escaping" and only for ['"\$(]
+ // (basically what's necessary inside a double-quoted literal plus the
+ // single quote).
+ //
+ lexer l (is, path ("<cmdline>"), "\'\"\\$(");
// The first token should be a name, either the variable name or the
// scope qualification.
@@ -143,7 +148,7 @@ namespace build2
if (c == '!' && !dir.empty ())
fail << "scope-qualified global override of variable " << n;
- variable_visibility v (c == '/' ? variable_visibility::scope :
+ variable_visibility v (c == '/' ? variable_visibility::scope :
c == '%' ? variable_visibility::project :
variable_visibility::normal);
diff --git a/build2/lexer b/build2/lexer
index c856344..42222e6 100644
--- a/build2/lexer
+++ b/build2/lexer
@@ -37,10 +37,19 @@ namespace build2
class lexer: protected butl::char_scanner
{
public:
+ // If escape is not NULL then only escape sequences with characters from
+ // this string are considered "effective escapes" with all others passed
+ // through as is. Note that the escape string is not copied.
+ //
lexer (istream& is,
const path& name,
+ const char* escapes = nullptr,
void (*processor) (token&, const lexer&) = nullptr)
- : char_scanner (is), fail (name), processor_ (processor), sep_ (false)
+ : char_scanner (is),
+ fail (name),
+ escapes_ (escapes),
+ processor_ (processor),
+ sep_ (false)
{
mode (lexer_mode::normal);
}
@@ -100,9 +109,6 @@ namespace build2
bool
skip_spaces ();
- xchar
- escape ();
-
// Diagnostics.
//
private:
@@ -120,8 +126,10 @@ namespace build2
private:
fail_mark fail;
+ const char* escapes_;
void (*processor_) (token&, const lexer&);
+
struct state
{
lexer_mode mode;
diff --git a/build2/lexer.cxx b/build2/lexer.cxx
index 773cd88..84e972e 100644
--- a/build2/lexer.cxx
+++ b/build2/lexer.cxx
@@ -4,6 +4,8 @@
#include <build2/lexer>
+#include <cstring> // strchr()
+
using namespace std;
namespace build2
@@ -309,10 +311,23 @@ namespace build2
if (c == '\\')
{
get ();
- c = escape ();
- if (c != '\n') // Ignore.
- lexeme += c;
- continue;
+ xchar e (peek ());
+
+ if (escapes_ == nullptr ||
+ (!eos (e) && strchr (escapes_, e) != nullptr))
+ {
+ get ();
+
+ if (eos (e))
+ fail (e) << "unterminated escape sequence";
+
+ if (e != '\n') // Ignore.
+ lexeme += e;
+
+ continue;
+ }
+ else
+ unget (c); // Treat as a normal character.
}
// If we are quoted, these are ordinary characters.
@@ -484,17 +499,6 @@ namespace build2
return r;
}
- lexer::xchar lexer::
- escape ()
- {
- xchar c (get ());
-
- if (eos (c))
- fail (c) << "unterminated escape sequence";
-
- return c;
- }
-
location_prologue lexer::fail_mark_base::
operator() (const xchar& c) const
{
diff --git a/build2/parser.cxx b/build2/parser.cxx
index 99e4227..a64a94e 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -2680,7 +2680,10 @@ namespace build2
{
path_ = &name;
- lexer l (is, *path_, &paren_processor);
+ // We do "effective escaping" and only for ['"\$(] (basically what's
+ // necessary inside a double-quoted literal plus the single quote).
+ //
+ lexer l (is, *path_, "\'\"\\$(", &paren_processor);
lexer_ = &l;
target_ = nullptr;
scope_ = root_ = global_scope;