Skip to content

Commit 0b73bf3

Browse files
committed
Make path quoting algorithm available for external use
Needed in libappimage.
1 parent 9a6cf8d commit 0b73bf3

File tree

3 files changed

+66
-27
lines changed

3 files changed

+66
-27
lines changed

include/XdgUtils/DesktopEntry/DesktopEntryExecValue.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ namespace XdgUtils {
4848
*/
4949
void remove(int pos);
5050

51+
/**
52+
* Quote path for use in desktop entry.
53+
* For use with keys like Exec, TryExec.
54+
* @param string
55+
*/
56+
static std::string quotePath(const std::string& string);
57+
5158
private:
5259
struct Priv;
5360
std::unique_ptr<Priv> priv;

src/DesktopEntry/DesktopEntryExecValue.cpp

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@
55

66
// local
77
#include <XdgUtils/DesktopEntry/DesktopEntryExecValue.h>
8-
#include <set>
8+
#include <algorithm>
99

1010
namespace XdgUtils {
1111
namespace DesktopEntry {
1212
struct DesktopEntryExecValue::Priv {
1313
std::vector<std::string> sections;
14-
std::string escapedCharacters = "\"`$\\";
15-
std::string reservedCharacters = " \t\n\"\'\\<>~|&;$*?#()`";
1614

1715
void parse(const std::string& value) {
1816
// marks when the next char was escaped
@@ -66,38 +64,21 @@ namespace XdgUtils {
6664

6765
std::string dump() {
6866
bool firstSection = true;
67+
6968
std::stringstream res;
69+
7070
for (const auto& section: sections) {
71-
if (firstSection)
71+
if (firstSection) {
7272
firstSection = false;
73-
else
73+
} else {
7474
res << " ";
75+
}
7576

76-
if (containsReservedCharacter(section)) {
77-
// dump escaped section
78-
res << "\"";
79-
for (const auto& c: section) {
80-
if (escapedCharacters.find(c) != std::string::npos)
81-
res << "\\";
82-
res << c;
83-
}
84-
res << "\"";
85-
86-
} else
87-
res << section;
88-
77+
res << quotePath(section);
8978
}
90-
return res.str();
91-
}
9279

93-
bool containsReservedCharacter(const std::string& string) {
94-
for (const auto& c: string)
95-
if (reservedCharacters.find(c) != std::string::npos)
96-
return true;
97-
98-
return false;
80+
return res.str();
9981
}
100-
10182
};
10283

10384
DesktopEntryExecValue::DesktopEntryExecValue() : priv(new Priv()) {}
@@ -127,6 +108,37 @@ namespace XdgUtils {
127108
void DesktopEntryExecValue::remove(int pos) {
128109
priv->sections.erase(priv->sections.begin() + pos);
129110
}
111+
112+
std::string DesktopEntryExecValue::quotePath(const std::string& string) {
113+
// these characters need to be escaped with a backslash (\)
114+
static const std::string charactersToEscape = "\"`$\\";
115+
// the following characters don't require escaping but can be used fine with simple quoting
116+
static const std::string charactersWhichRequireQuoting = " \t\n\'<>~|&;*?#()";
117+
// we may need the combined list more than once and therefore cache it in a variable
118+
static const std::string reservedCharacters = charactersWhichRequireQuoting + charactersToEscape;
119+
120+
std::ostringstream res;
121+
122+
// some paths don't need to be quoted
123+
// in that case, we don't prepend/append quotes
124+
bool needsToBeQuoted = std::find_if(string.begin(), string.end(), [](const char c) {
125+
return reservedCharacters.find(c) != std::string::npos;
126+
}) != string.end();
127+
128+
if (!needsToBeQuoted) {
129+
return string;
130+
}
131+
132+
res << "\"";
133+
for (const auto& c: string) {
134+
if (charactersToEscape.find(c) != std::string::npos)
135+
res << "\\";
136+
res << c;
137+
}
138+
res << "\"";
139+
140+
return res.str();
141+
}
130142
}
131143
}
132144

tests/DesktopEntry/TestDesktopEntryExecValue.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,23 @@ TEST(TestDesktopDesktopEntryExecValue, remove) {
5555
ASSERT_EQ(stringList.size(), 1);
5656
ASSERT_EQ(stringList[0], "2");
5757
}
58+
59+
TEST(TestDesktopDesktopEntryExecValue, testQuoteComplexPath) {
60+
const std::string testPath = "/home/abc/Applications/a \t <>~ | % ;#()? \" b c $ `.AppImage";
61+
const std::string expectedQuotedPath = "\"/home/abc/Applications/a \t <>~ | % ;#()? \\\" b c \\$ \\`.AppImage\"";
62+
63+
const auto quotedPath = DesktopEntryExecValue::quotePath(testPath);
64+
65+
std::cout << quotedPath << std::endl;
66+
std::cout << expectedQuotedPath << std::endl;
67+
68+
EXPECT_EQ(quotedPath, expectedQuotedPath);
69+
}
70+
71+
TEST(TestDesktopDesktopEntryExecValue, testQuoteSimplePath) {
72+
const std::string testPath = R"(/home/abc/Applications/abc.AppImage)";
73+
74+
const auto quotedPath = DesktopEntryExecValue::quotePath(testPath);
75+
76+
EXPECT_EQ(quotedPath, testPath);
77+
}

0 commit comments

Comments
 (0)