Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions include/hyprutils/string/VarList.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ namespace Hyprutils {
class CVarList {
public:
/** Split string into arg list
@param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args
@param delim if delimiter is 's', use std::isspace
@param removeEmpty remove empty args from argv
* @param in The string to split
* @param lastArgNo The number of arguments to split into
* @param delim The delimiter to use for splitting
* @param removeEmpty Whether to remove empty arguments
*/
CVarList(const std::string& in, const size_t lastArgNo = 0, const char delim = ',', const bool removeEmpty = false);

/** Split string into arg list with escape handling
* @param in The string to split
* @param lastArgNo The number of arguments to split into
* @param delim The delimiter to use for splitting
* @param removeEmpty Whether to remove empty arguments
* @param handleEscape Whether to handle escape characters
*/
CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty, const bool handleEscape);

~CVarList() = default;

size_t size() const {
Expand Down
58 changes: 45 additions & 13 deletions src/string/VarList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,56 @@

using namespace Hyprutils::String;

Hyprutils::String::CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) {
if (!removeEmpty && in.empty())
// Original constructor calls the extended one with handleEscape = false
Hyprutils::String::CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) :
CVarList(in, lastArgNo, delim, removeEmpty, false) {}

// Extended constructor with escape handling parameter
Hyprutils::String::CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty, const bool handleEscape) {
if (!removeEmpty && in.empty()) {
m_vArgs.emplace_back("");
return;
}

std::string args{in};
size_t idx = 0;
size_t pos = 0;
std::ranges::replace_if(args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
std::vector<std::pair<size_t, size_t>> argIndices;
size_t currentStart = 0;
size_t idx = 0;

for (const auto& s : args | std::views::split(0)) {
if (removeEmpty && s.empty())
for (size_t i = 0; i < in.length(); ++i) {
if (handleEscape && in[i] == '\\' && i + 1 < in.length()) {
i++;
continue;
if (++idx == lastArgNo) {
m_vArgs.emplace_back(trim(in.substr(pos)));
break;
}
pos += s.size() + 1;
m_vArgs.emplace_back(trim(s.data()));

const char c = in[i];
const bool isDelim = (delim == 's' ? std::isspace(c) : c == delim);

if (isDelim) {
if (!removeEmpty || i > currentStart) {
argIndices.emplace_back(currentStart, i);
idx++;
}

currentStart = i + 1;

if (idx == lastArgNo - 1) {
argIndices.emplace_back(i + 1, in.length());
break;
}
}
}

if (currentStart < in.length() && (!removeEmpty || currentStart < in.length()))
argIndices.emplace_back(currentStart, in.length());

m_vArgs.reserve(argIndices.size());
for (const auto& [start, end] : argIndices) {
if (handleEscape) {
std::string segment = in.substr(start, end - start);
replaceInString(segment, "\\", "");
m_vArgs.emplace_back(trim(segment));
} else
m_vArgs.emplace_back(trim(in.substr(start, end - start)));
}
}

Expand Down
9 changes: 9 additions & 0 deletions tests/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ int main(int argc, char** argv, char** envp) {
EXPECT(list[0], "hello");
EXPECT(list[1], "world!");

CVarList list2("test:test\\:test", 0, ':', true, true);
EXPECT(list2[0], "test");
EXPECT(list2[1], "test:test");

CVarList list3("test:test\\:test", 0, ':', true);
EXPECT(list3[0], "test");
EXPECT(list3[1], "test\\");
EXPECT(list3[2], "test");

CConstVarList listConst("hello world!", 0, 's', true);
EXPECT(listConst[0], "hello");
EXPECT(listConst[1], "world!");
Expand Down