From 9d860f46631c1567c62336fcb25cf7b8090855a4 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 6 Dec 2021 11:16:22 +0200 Subject: Recognize absolute Windows paths in make parser --- libbuild2/make-parser.cxx | 63 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 19 deletions(-) (limited to 'libbuild2/make-parser.cxx') diff --git a/libbuild2/make-parser.cxx b/libbuild2/make-parser.cxx index cab81d0..c6c077f 100644 --- a/libbuild2/make-parser.cxx +++ b/libbuild2/make-parser.cxx @@ -3,23 +3,21 @@ #include +#include // strchr() + #include namespace build2 { auto make_parser:: - next (const string& l, - size_t& p, - const location& ll, - bool strict) -> pair + next (const string& l, size_t& p, const location& ll) -> pair { assert (state != end); - pair r ( - next (l, p, !strict ? state == prereqs : optional ())); - type t (state == prereqs ? type::prereq : type::target); + pair r (next (l, p, t)); + // Deal with the end. // if (r.second) @@ -67,8 +65,15 @@ namespace build2 } } + // Note: backslash must be first. + // + // Note also that, at least in GNU make 4.1, `%` seems to be unescapable + // if appears in a target and literal if in a prerequisite. + // + static const char escapable[] = "\\ :#"; + pair make_parser:: - next (const string& l, size_t& p, optional prereq) + next (const string& l, size_t& p, type) { size_t n (l.size ()); @@ -87,9 +92,36 @@ namespace build2 // // @@ Can't we do better for the (common) case where nothing is escaped? // - for (char c, q (prereq && *prereq ? '\0' : ':'); - p != n && (c = l[p]) != ' ' && c != q; ) +#ifdef _WIN32 + size_t b (p); +#endif + + for (char c; p != n && (c = l[p]) != ' '; r += c) { + if (c == ':') + { +#ifdef _WIN32 + // See if this colon is part of the driver letter component in an + // absolute Windows path. + // + // Note that here we assume we are not dealing with directories (in + // which case c: would be a valid path) and thus an absolute path is + // at least 4 characters long (e.g., c:\x). + // + if (p == b + 1 && // Colon is second character. + alpha (l[b]) && // First is drive letter. + p + 2 < n && // At least two more characters after colon. + ((l[p + 1] == '/') || // Next is directory separator. + (l[p + 1] == '\\' && // But not part of a non-\\ escape sequence. + strchr (escapable + 1, l[p + 2]) == nullptr))) + { + ++p; + continue; + } +#endif + break; + } + // If we have another character, then handle the escapes. // if (++p != n) @@ -99,13 +131,8 @@ namespace build2 // This may or may not be an escape sequence depending on whether // what follows is "escapable". // - switch (c = l[p]) - { - case '\\': - case ' ': - case ':': ++p; break; - default: c = '\\'; // Restore. - } + if (strchr (escapable, l[p]) != nullptr) + c = l[p++]; } else if (c == '$') { @@ -122,8 +149,6 @@ namespace build2 --p; break; } - - r += c; } // Skip trailing spaces. -- cgit v1.1