@@ -54,64 +54,85 @@ static std::string format(const char *format, const tsl::ordered_map<std::string
5454 return s;
5555}
5656
57- static std::vector<std::string> expand_cmake_path (const fs::path &name, const fs::path &toml_dir, bool is_root_project) {
58-
59- auto const extract_suffix = [](const fs::path &base, const fs::path &full) {
60- auto fullpath = full.string ();
61- auto base_len = base.string ().length ();
62- return fullpath.substr (base_len + 1 , fullpath.length () - base_len);
63- };
64- auto const extract_path_parts = [](const fs::path &file_path) -> std::pair<std::string, std::string> {
65- const auto path_as_string = file_path.string ();
66- auto const dot_position = path_as_string.find (' .' );
67- if (dot_position != std::string::npos) {
68- return {path_as_string.substr (0 , dot_position), path_as_string.substr (dot_position)};
57+ static std::vector<fs::path> expand_cmake_path (const fs::path &source_path, const fs::path &toml_dir, bool is_root_project) {
58+ auto is_subdir = [](fs::path p, const fs::path &root) {
59+ while (true ) {
60+ if (p == root) {
61+ return true ;
62+ }
63+ auto parent = p.parent_path ();
64+ if (parent == p) {
65+ break ;
66+ }
67+ p = parent;
6968 }
70- return {path_as_string, {}} ;
69+ return false ;
7170 };
72- auto const path_parts = extract_path_parts (name.filename ());
73- auto const &stem = path_parts.first ;
74- auto const &extension = path_parts.second ;
71+ if (!is_subdir (fs::absolute (toml_dir / source_path), toml_dir)) {
72+ throw std::runtime_error (" Path traversal is not allowed: " + source_path.string ());
73+ }
74+
75+ // Split the path at the first period (since fs::path::stem() and fs::path::extension() split at the last period)
76+ std::string stem, extension;
77+ auto filename = source_path.filename ().string ();
78+ auto dot_position = filename.find (' .' );
79+ if (dot_position != std::string::npos) {
80+ stem = filename.substr (0 , dot_position);
81+ extension = filename.substr (dot_position);
82+ } else {
83+ stem = filename;
84+ }
7585
76- if (is_root_project && stem == " **" && name == name. filename ()) {
77- throw std::runtime_error (" Recursive globbing not allowed in project root: " + name .string ());
86+ if (is_root_project && stem == " **" && !source_path. has_parent_path ()) {
87+ throw std::runtime_error (" Recursive globbing not allowed in project root: " + source_path .string ());
7888 }
7989
80- std::vector<std::string> temp;
90+ auto has_extension = [](const fs::path &file_path, const std::string &extension) {
91+ auto path = file_path.string ();
92+ return path.rfind (extension) == path.length () - extension.length ();
93+ };
94+
95+ std::vector<fs::path> paths;
8196 if (stem == " *" ) {
82- for (const auto &f : fs::directory_iterator (toml_dir / name .parent_path (), fs::directory_options::follow_directory_symlink)) {
83- if (!f.is_directory () && extract_path_parts (f.path (). filename ()). second == extension) {
84- temp .push_back (extract_suffix (toml_dir, f ));
97+ for (const auto &f : fs::directory_iterator (toml_dir / source_path .parent_path (), fs::directory_options::follow_directory_symlink)) {
98+ if (!f.is_directory () && has_extension (f.path (), extension) ) {
99+ paths .push_back (fs::relative (f, toml_dir ));
85100 }
86101 }
87102 } else if (stem == " **" ) {
88- for (const auto &f : fs::recursive_directory_iterator (toml_dir / name.parent_path (), fs::directory_options::follow_directory_symlink)) {
89- if (!f.is_directory () && extract_path_parts (f.path ().filename ()).second == extension) {
90- temp.push_back (extract_suffix (toml_dir, f.path ()));
103+ for (const auto &f :
104+ fs::recursive_directory_iterator (toml_dir / source_path.parent_path (), fs::directory_options::follow_directory_symlink)) {
105+ if (!f.is_directory () && has_extension (f.path (), extension)) {
106+ paths.push_back (fs::relative (f, toml_dir));
91107 }
92108 }
93109 } else {
94- temp .push_back (name. string () );
110+ paths .push_back (source_path );
95111 }
96- // Normalize all paths to work with CMake (it needs a / on Windows as well)
97- for (auto &path : temp) {
98- std::replace (path.begin (), path.end (), ' \\ ' , ' /' );
99- }
100- // Sort paths alphabetically for consistent cross-OS generation
101- std::sort (temp.begin (), temp.end ());
102- return temp;
112+
113+ return paths;
103114}
104115
105116static std::vector<std::string> expand_cmake_paths (const std::vector<std::string> &sources, const fs::path &toml_dir, bool is_root_project) {
106- // TODO: add duplicate checking
107- std::vector<std::string> result;
117+ std::vector<std::string> paths;
108118 for (const auto &src : sources) {
109119 auto expanded = expand_cmake_path (src, toml_dir, is_root_project);
110120 for (const auto &f : expanded) {
111- result .push_back (f);
121+ paths .push_back (f. string () );
112122 }
113123 }
114- return result;
124+
125+ // Normalize all paths to work with CMake (it needs a / on Windows as well)
126+ for (auto &path : paths) {
127+ std::replace (path.begin (), path.end (), ' \\ ' , ' /' );
128+ }
129+
130+ // Sort paths alphabetically for consistent cross-OS generation
131+ std::sort (paths.begin (), paths.end ());
132+
133+ // TODO: remove duplicates
134+
135+ return paths;
115136}
116137
117138static void create_file (const fs::path &path, const std::string &contents) {
@@ -681,7 +702,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
681702
682703 parser::Project project (parent_project, path, false );
683704
684- for (auto const &lang : project.project_languages ) {
705+ for (const auto &lang : project.project_languages ) {
685706 if (known_languages.find (lang) == known_languages.end ()) {
686707 if (project.project_allow_unknown_languages ) {
687708 printf (" [warning] Unknown language '%s' specified\n " , lang.c_str ());
0 commit comments