This commit is contained in:
374
source/src/utils/utils.cpp
Normal file
374
source/src/utils/utils.cpp
Normal file
@ -0,0 +1,374 @@
|
||||
#include "utils.hpp"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <regex>
|
||||
#include <random>
|
||||
|
||||
namespace dropshell {
|
||||
|
||||
void maketitle(const std::string& title) {
|
||||
std::cout << std::string(title.length() + 4, '-') << std::endl;
|
||||
std::cout << "| " << title << " |" << std::endl;
|
||||
std::cout << std::string(title.length() + 4, '-') << std::endl;
|
||||
}
|
||||
|
||||
bool replace_line_in_file(const std::string& file_path, const std::string& search_string, const std::string& replacement_line) {
|
||||
std::ifstream input_file(file_path);
|
||||
std::vector<std::string> file_lines;
|
||||
std::string line;
|
||||
|
||||
if (!input_file.is_open()) {
|
||||
std::cerr << "Error: Unable to open file: " << file_path << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
while (std::getline(input_file, line)) {
|
||||
if (line.find(search_string) != std::string::npos)
|
||||
file_lines.push_back(replacement_line);
|
||||
else
|
||||
file_lines.push_back(line);
|
||||
}
|
||||
input_file.close();
|
||||
|
||||
std::ofstream output_file(file_path);
|
||||
if (!output_file.is_open())
|
||||
{
|
||||
std::cerr << "Error: Unable to open file: " << file_path << std::endl;
|
||||
return false;
|
||||
}
|
||||
for (const auto& modified_line : file_lines)
|
||||
output_file << modified_line << "\n";
|
||||
output_file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string trim(std::string str) {
|
||||
// Trim leading whitespace
|
||||
str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
|
||||
// Trim trailing whitespace
|
||||
str.erase(std::find_if(str.rbegin(), str.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), str.end());
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string dequote(std::string str)
|
||||
{
|
||||
if (str.length() < 2)
|
||||
return str;
|
||||
if (str.front() == '"' && str.back() == '"') {
|
||||
return str.substr(1, str.length() - 2);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string quote(std::string str)
|
||||
{
|
||||
return "\""+str+"\"";
|
||||
}
|
||||
|
||||
std::string halfquote(std::string str)
|
||||
{
|
||||
return "'" + str + "'";
|
||||
}
|
||||
|
||||
std::string escapequotes(std::string str)
|
||||
{
|
||||
return std::regex_replace(str, std::regex("\""), "\\\"");
|
||||
}
|
||||
|
||||
std::string multi2string(std::vector<std::string> values)
|
||||
{
|
||||
std::string result;
|
||||
for (const auto& value : values) {
|
||||
// remove any " contained in the string value, if present
|
||||
result += dequote(trim(value)) + ",";
|
||||
}
|
||||
if (!result.empty())
|
||||
result.pop_back(); // Remove the last comma
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> string2multi(std::string values)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
values = dequote(trim(values));
|
||||
|
||||
// Return values separated by commas, but ignore commas within quotes
|
||||
bool inside_quotes = false;
|
||||
std::string current_item;
|
||||
|
||||
for (char c : values) {
|
||||
if (c == '"') {
|
||||
inside_quotes = !inside_quotes;
|
||||
} else if (c == ',' && !inside_quotes) {
|
||||
if (!current_item.empty()) {
|
||||
std::string final = dequote(trim(current_item));
|
||||
if (!final.empty())
|
||||
result.push_back(final);
|
||||
current_item.clear();
|
||||
}
|
||||
} else {
|
||||
current_item += c;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last item if not empty
|
||||
if (!current_item.empty()) {
|
||||
std::string final = dequote(trim(current_item));
|
||||
if (!final.empty())
|
||||
result.push_back(final);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int str2int(const std::string &str)
|
||||
{
|
||||
try {
|
||||
return std::stoi(str);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error: Invalid integer string: [" << str << "]" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void recursive_copy(const std::string & source, const std::string & destination) {
|
||||
try {
|
||||
if (std::filesystem::is_directory(source)) {
|
||||
if (!std::filesystem::exists(destination)) {
|
||||
std::filesystem::create_directory(destination);
|
||||
}
|
||||
for (const auto& entry : std::filesystem::directory_iterator(source)) {
|
||||
recursive_copy(entry.path(), destination / entry.path().filename());
|
||||
}
|
||||
} else if (std::filesystem::is_regular_file(source)) {
|
||||
std::filesystem::copy(source, destination, std::filesystem::copy_options::overwrite_existing);
|
||||
}
|
||||
} catch (const std::filesystem::filesystem_error& ex) {
|
||||
std::cerr << "Error copying " << source << " to " << destination << ": " << ex.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void ensure_directories_exist(std::vector<std::string> directories)
|
||||
{
|
||||
for (const auto& directory : directories) {
|
||||
if (!std::filesystem::exists(directory)) {
|
||||
std::filesystem::create_directories(directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/
|
||||
void constructLps(const std::string &pat, std::vector<int> &lps) {
|
||||
|
||||
// len stores the length of longest prefix which
|
||||
// is also a suffix for the previous index
|
||||
int len = 0;
|
||||
|
||||
// lps[0] is always 0
|
||||
lps[0] = 0;
|
||||
|
||||
int i = 1;
|
||||
while (i < pat.length()) {
|
||||
|
||||
// If characters match, increment the size of lps
|
||||
if (pat[i] == pat[len]) {
|
||||
len++;
|
||||
lps[i] = len;
|
||||
i++;
|
||||
}
|
||||
|
||||
// If there is a mismatch
|
||||
else {
|
||||
if (len != 0) {
|
||||
|
||||
// Update len to the previous lps value
|
||||
// to avoid reduntant comparisons
|
||||
len = lps[len - 1];
|
||||
}
|
||||
else {
|
||||
|
||||
// If no matching prefix found, set lps[i] to 0
|
||||
lps[i] = 0;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> search(const std::string &pat, const std::string &txt) {
|
||||
int n = txt.length();
|
||||
int m = pat.length();
|
||||
|
||||
std::vector<int> lps(m);
|
||||
std::vector<int> res;
|
||||
|
||||
constructLps(pat, lps);
|
||||
|
||||
// Pointers i and j, for traversing
|
||||
// the text and pattern
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
while (i < n) {
|
||||
|
||||
// If characters match, move both pointers forward
|
||||
if (txt[i] == pat[j]) {
|
||||
i++;
|
||||
j++;
|
||||
|
||||
// If the entire pattern is matched
|
||||
// store the start index in result
|
||||
if (j == m) {
|
||||
res.push_back(i - j);
|
||||
|
||||
// Use LPS of previous index to
|
||||
// skip unnecessary comparisons
|
||||
j = lps[j - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// If there is a mismatch
|
||||
else {
|
||||
|
||||
// Use lps value of previous index
|
||||
// to avoid redundant comparisons
|
||||
if (j != 0)
|
||||
j = lps[j - 1];
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int count_substring(const std::string &substring, const std::string &text) {
|
||||
std::vector<int> positions = search(substring, text);
|
||||
return positions.size();
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& str, const std::string& delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
|
||||
while ((end = str.find(delimiter, start)) != std::string::npos) {
|
||||
tokens.push_back(str.substr(start, end - start));
|
||||
start = end + delimiter.length();
|
||||
}
|
||||
|
||||
// Add the last token
|
||||
tokens.push_back(str.substr(start));
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
std::string replace_with_environment_variables_like_bash(std::string str) {
|
||||
// Combined regex pattern for both ${var} and $var formats
|
||||
std::regex var_pattern("\\$(?:\\{([^}]+)\\}|([a-zA-Z0-9_]+))");
|
||||
std::string result = str;
|
||||
std::smatch match;
|
||||
|
||||
while (std::regex_search(result, match, var_pattern)) {
|
||||
// match[1] will contain capture from ${var} format
|
||||
// match[2] will contain capture from $var format
|
||||
std::string var_name = match[1].matched ? match[1].str() : match[2].str();
|
||||
|
||||
// Get value from system environment variables
|
||||
const char* env_value = std::getenv(var_name.c_str());
|
||||
std::string value = env_value ? env_value : "";
|
||||
|
||||
result = result.replace(match.position(), match.length(), value);
|
||||
}
|
||||
|
||||
// dequote the result
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string random_alphanumeric_string(int length)
|
||||
{
|
||||
static std::mt19937 generator(std::random_device{}());
|
||||
static const std::string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
std::uniform_int_distribution<> distribution(0, chars.size() - 1);
|
||||
std::string random_string;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
random_string += chars[distribution(generator)];
|
||||
}
|
||||
|
||||
return random_string;
|
||||
}
|
||||
|
||||
std::string requote(std::string str) {
|
||||
return quote(trim(dequote(trim(str))));
|
||||
}
|
||||
|
||||
|
||||
int die(const std::string & msg) {
|
||||
std::cerr << msg << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string safearg(const std::vector<std::string> & args, int index)
|
||||
{
|
||||
if (index<0 || index >= args.size()) return "";
|
||||
return args[index];
|
||||
}
|
||||
|
||||
std::string safearg(int argc, char *argv[], int index)
|
||||
{
|
||||
if (index<0 || index >= argc) return "";
|
||||
return argv[index];
|
||||
}
|
||||
|
||||
|
||||
void print_left_aligned(const std::string & str, int width) {
|
||||
std::cout << left_align(str, width);
|
||||
}
|
||||
|
||||
void print_centered(const std::string & str, int width) {
|
||||
std::cout << center_align(str, width);
|
||||
}
|
||||
|
||||
void print_right_aligned(const std::string & str, int width) {
|
||||
std::cout << right_align(str, width);
|
||||
}
|
||||
|
||||
|
||||
std::string left_align(const std::string & str, int width) {
|
||||
if (static_cast<int>(str.size()) >= width)
|
||||
return str;
|
||||
return str + std::string(width - str.size(), ' ');
|
||||
}
|
||||
|
||||
std::string right_align(const std::string & str, int width) {
|
||||
if (static_cast<int>(str.size()) >= width)
|
||||
return str;
|
||||
return std::string(width - str.size(), ' ') + str;
|
||||
}
|
||||
|
||||
std::string center_align(const std::string & str, int width) {
|
||||
int pad = width - static_cast<int>(str.size());
|
||||
if (pad <= 0)
|
||||
return str;
|
||||
int pad_left = pad / 2;
|
||||
int pad_right = pad - pad_left;
|
||||
return std::string(pad_left, ' ') + str + std::string(pad_right, ' ');
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace dropshell
|
Reference in New Issue
Block a user