@@ -54,57 +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- std::vector<std::string> temp;
59-
60- auto extract_suffix = [](const fs::path &base, const fs::path &full) {
61- auto fullpath = full.string ();
62- auto base_len = base.string ().length ();
63- auto delet = fullpath.substr (base_len + 1 , fullpath.length () - base_len);
64- return delet;
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;
68+ }
69+ return false ;
6570 };
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+ }
6674
67- auto stem = name.filename ().stem ().string ();
68- auto ext = name.extension ();
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+ }
6985
70- if (is_root_project && stem == " **" && name == name. filename ()) {
71- 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 ());
7288 }
7389
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;
7496 if (stem == " *" ) {
75- for (const auto &f : fs::directory_iterator (toml_dir / name .parent_path (), fs::directory_options::follow_directory_symlink)) {
76- if (!f.is_directory () && f.path (). extension () == ext ) {
77- 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 ));
78100 }
79101 }
80102 } else if (stem == " **" ) {
81- for (const auto &f : fs::recursive_directory_iterator (toml_dir / name.parent_path (), fs::directory_options::follow_directory_symlink)) {
82- if (!f.is_directory () && f.path ().extension () == ext) {
83- 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));
84107 }
85108 }
86109 } else {
87- temp .push_back (name. string () );
110+ paths .push_back (source_path );
88111 }
89- // Normalize all paths to work with CMake (it needs a / on Windows as well)
90- for (auto &path : temp) {
91- std::replace (path.begin (), path.end (), ' \\ ' , ' /' );
92- }
93- // Sort paths alphabetically for consistent cross-OS generation
94- std::sort (temp.begin (), temp.end ());
95- return temp;
112+
113+ return paths;
96114}
97115
98116static std::vector<std::string> expand_cmake_paths (const std::vector<std::string> &sources, const fs::path &toml_dir, bool is_root_project) {
99- // TODO: add duplicate checking
100- std::vector<std::string> result;
117+ std::vector<std::string> paths;
101118 for (const auto &src : sources) {
102119 auto expanded = expand_cmake_path (src, toml_dir, is_root_project);
103120 for (const auto &f : expanded) {
104- result .push_back (f);
121+ paths .push_back (f. string () );
105122 }
106123 }
107- 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;
108136}
109137
110138static void create_file (const fs::path &path, const std::string &contents) {
@@ -674,7 +702,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
674702
675703 parser::Project project (parent_project, path, false );
676704
677- for (auto const &lang : project.project_languages ) {
705+ for (const auto &lang : project.project_languages ) {
678706 if (known_languages.find (lang) == known_languages.end ()) {
679707 if (project.project_allow_unknown_languages ) {
680708 printf (" [warning] Unknown language '%s' specified\n " , lang.c_str ());
0 commit comments