3434
3535/* *
3636 * Path matching rules:
37- * - All patterns are simplified first (path separators vary by platform):
37+ * - All patterns are canonicalized (path separators vary by platform):
3838 * - '/./' => '/'
3939 * - '/dir/../' => '/'
4040 * - '//' => '/'
41- * - Trailing slashes are removed
41+ * - Trailing slashes are removed (root slash is preserved)
4242 * - Patterns can contain globs:
4343 * - '**' matches any number of characters including path separators.
4444 * - '*' matches any number of characters except path separators.
4545 * - '?' matches any single character except path separators.
4646 * - If a pattern looks like an absolute path (e.g. starts with '/', but varies by platform):
47- * - Match all files where the pattern matches the start of the file's simplified absolute path up until a path
47+ * - Match all files where the pattern matches the start of the file's canonical absolute path up until a path
4848 * separator or the end of the pathname.
49- * - If a pattern starts with '.':
49+ * - If a pattern looks like a relative path, i.e. is '.' or '..', or
50+ * starts with '.' or '..' followed by a path separator:
5051 * - The pattern is interpreted as a path relative to `basepath` and then converted to an absolute path and
5152 * treated as such according to the above procedure. If the pattern is relative to some other directory, it should
5253 * be modified to be relative to `basepath` first (this should be done with patterns in project files, for example).
5354 * - Otherwise:
54- * - Match all files where the pattern matches any part of the file's simplified absolute path up until a
55+ * - Match all files where the pattern matches any part of the file's canonical absolute path up until a
5556 * path separator or the end of the pathname, and the matching part directly follows a path separator.
5657 **/
5758
@@ -66,15 +67,15 @@ class CPPCHECKLIB PathMatch {
6667 *
6768 * scase: Case sensitive.
6869 * icase: Case insensitive.
69- ** /
70+ */
7071 enum class Mode : std::uint8_t {
7172 scase,
7273 icase,
7374 };
7475
7576 /* *
7677 * @brief The default mode for the current platform.
77- ** /
78+ */
7879#ifdef _WIN32
7980 static constexpr Mode platform_mode = Mode::icase;
8081#else
@@ -109,6 +110,44 @@ class CPPCHECKLIB PathMatch {
109110 */
110111 static bool match (const std::string &pattern, const std::string &path, const std::string &basepath = std::string(), Mode mode = platform_mode);
111112
113+ /* *
114+ * @brief Check if a pattern is a relative path name.
115+ *
116+ * @param pattern Pattern to check.
117+ * @return true if the pattern has the form of a relative path name pattern.
118+ */
119+ static bool isRelativePattern (const std::string &pattern)
120+ {
121+ if (pattern.empty () || pattern[0 ] != ' .' )
122+ return false ;
123+
124+ if (pattern.size () < 2 || pattern[1 ] == ' /' || pattern[1 ] == ' \\ ' )
125+ return true ;
126+
127+ if (pattern[1 ] != ' .' )
128+ return false ;
129+
130+ if (pattern.size () < 3 || pattern[2 ] == ' /' || pattern[2 ] == ' \\ ' )
131+ return true ;
132+
133+ return false ;
134+ }
135+
136+ /* *
137+ * @brief Join a pattern with a base path.
138+ *
139+ * @param basepath The base path to join the pattern to.
140+ * @param pattern The pattern to join.
141+ * @return The pattern appended to the base path with a separator if the pattern is a relative
142+ * path name, otherwise just returns pattern.
143+ */
144+ static std::string joinRelativePattern (const std::string &basepath, const std::string &pattern)
145+ {
146+ if (isRelativePattern (pattern))
147+ return Path::join (basepath, pattern);
148+ return pattern;
149+ }
150+
112151private:
113152 friend class TestPathMatch ;
114153 class PathIterator ;
@@ -142,16 +181,16 @@ class CPPCHECKLIB PathMatch {
142181 **/
143182class PathMatch ::PathIterator {
144183public:
145- /* Create from a pattern and base path, patterns must begin with '.' to be considered relative */
146- static PathIterator from_pattern (const std::string &pattern, const std::string &basepath, bool icase)
184+ /* Create from a pattern and base path */
185+ static PathIterator fromPattern (const std::string &pattern, const std::string &basepath, bool icase)
147186 {
148- if (!pattern. empty () && pattern[ 0 ] == ' . ' )
187+ if (isRelativePattern ( pattern) )
149188 return PathIterator (basepath.c_str (), pattern.c_str (), icase);
150189 return PathIterator (pattern.c_str (), nullptr , icase);
151190 }
152191
153192 /* Create from path and base path */
154- static PathIterator from_path (const std::string &path, const std::string &basepath, bool icase)
193+ static PathIterator fromPath (const std::string &path, const std::string &basepath, bool icase)
155194 {
156195 if (Path::isAbsolute (path))
157196 return PathIterator (path.c_str (), nullptr , icase);
@@ -270,7 +309,7 @@ class PathMatch::PathIterator {
270309 nextc ();
271310 c = current ();
272311 if (c == ' /' ) {
273- /* Skip '<name> /../' */
312+ /* Skip 'dir /../' */
274313 nextc ();
275314 skips (false );
276315 while (mPos .l != 0 && current () != ' /' )
0 commit comments