/// /// \file rcpp.cc /// \author kyle /// \created 2023-10-12 /// \brief rcpp is a recursive copy by pattern program. /// /// \section COPYRIGHT /// /// Copyright 2023 K. Isom /// /// Permission to use, copy, modify, and/or distribute this software for /// any purpose with or without fee is hereby granted, provided that /// the above copyright notice and this permission notice appear in all /// copies. /// /// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL /// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED /// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE /// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL /// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA /// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER /// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR /// PERFORMANCE OF THIS SOFTWARE. /// #include #include #include #include #include "Flag.h" using namespace klib; #define ALIAS_FS namespace fs = std::filesystem typedef std::filesystem::path Path; #ifdef NO_TRACE static std::ofstream null("/dev/null"); #define TRACE null #else #define TRACE std::cout #endif int preflightDirectory(Path dst) { ALIAS_FS; if (!fs::exists(dst)) { fs::create_directory(dst); } else if (!fs::is_directory(dst)) { return -1; } return 0; } int preflightChecks(Path dst, Path src) { ALIAS_FS; if (preflightDirectory(dst) != 0) { return -1; } if (!fs::exists(src)) { return -1; } if (!fs::is_regular_file(src)) { return -1; } return 0; } int copyToPath(Path dst, Path src) { ALIAS_FS; TRACE << "copyToPath " << dst << " " << src << "\n"; if (preflightChecks(dst, src)) { return -1; } Path dstFile = dst / src.filename(); TRACE << "destination file: " << dstFile << "\n"; fs::copy(src, dst, fs::copy_options::update_existing); return 0; } int maybeCopyPattern(Path dst, Path entry, std::regex &pattern) { ALIAS_FS; TRACE << "maybeCopyPattern " << entry << "\n"; if (!fs::is_regular_file(entry)) { TRACE << "\tnot a file\n"; return 0; } if (!std::regex_search(entry.string(), pattern)) { TRACE << "\tpattern doesn't match\n"; return 0; } return (copyToPath(dst, entry)); } int recursiveCopyPattern(Path &dst, Path &src, std::regex &pattern) { ALIAS_FS; if (!fs::exists(src)) { return -1; } if (!fs::is_directory(src)) { return -1; } for (auto entry : fs::recursive_directory_iterator(src)) { TRACE << "found " << entry << "\n"; if (maybeCopyPattern(dst, entry, pattern)) { return -1; } } return 0; } int main(int argc, char *argv[]) { ALIAS_FS; auto regexOptions = std::regex_constants::optimize; Flags optArgs("rcpp", "recursively copy files by pattern"); optArgs.Register("-e", FlagType::Boolean, "pattern is a file extension"); optArgs.Register("-i", FlagType::Boolean, "case-insensitive maches"); if (optArgs.Parse(argc, argv)) { optArgs.Usage(std::cerr, 1); } if (optArgs.NumArgs() < 3) { optArgs.Usage(std::cerr, 1); } auto pattern = optArgs.Arg(0); auto destDir = fs::path(optArgs.Arg(optArgs.NumArgs() - 1)); bool value; if (optArgs.GetBool("-e", value) && value) { pattern = "\\." + pattern + "$"; } if (optArgs.GetBool("-i", value) && value) { regexOptions |= std::regex_constants::icase; } destDir = fs::absolute(destDir); TRACE << "argc: " << argc << "\n"; auto rePattern = std::regex(pattern); for (int i = 2; i < argc-1; i++) { TRACE << "[" << i << "] consider " << argv[i] << "\n"; auto src = fs::path(argv[i]); if (recursiveCopyPattern(destDir, src, rePattern) != 0) { TRACE << "failed\n"; } } return 0; }