From aadcd6642f8b077afb1ace6328bf948fba318868 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Fri, 28 Dec 2018 18:17:07 -0600 Subject: [PATCH 1/2] Allow setting new values to DestkopFileEntry --- include/linuxdeploy/desktopfile/desktopfileentry.h | 3 +++ src/desktopfileentry.cpp | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/include/linuxdeploy/desktopfile/desktopfileentry.h b/include/linuxdeploy/desktopfile/desktopfileentry.h index 3940668..beb2dc2 100644 --- a/include/linuxdeploy/desktopfile/desktopfileentry.h +++ b/include/linuxdeploy/desktopfile/desktopfileentry.h @@ -46,6 +46,9 @@ namespace linuxdeploy { // return entry's value const std::string& value() const; + // sets a new value + void setValue(const std::string& value); + public: // convert value to integer // throws BadLexicalCastError in case of type errors diff --git a/src/desktopfileentry.cpp b/src/desktopfileentry.cpp index a825bac..c2df317 100644 --- a/src/desktopfileentry.cpp +++ b/src/desktopfileentry.cpp @@ -115,5 +115,9 @@ namespace linuxdeploy { return list; } + + void DesktopFileEntry::setValue(const std::string& value) { + d->value = value; + } } } From d1b74ed077afbb2a0f1047d1ba41bafc04306a30 Mon Sep 17 00:00:00 2001 From: Alexis Lopez Zubieta Date: Fri, 28 Dec 2018 18:26:31 -0600 Subject: [PATCH 2/2] Add DesktopExectEntryTokenizer --- .../desktopfile/desktopexecentrytokenizer.h | 44 ++++++++++ src/CMakeLists.txt | 1 + src/desktopexecentrytokenizer.cpp | 86 +++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/test_desktopexecentrytokenizer.cpp | 58 +++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 include/linuxdeploy/desktopfile/desktopexecentrytokenizer.h create mode 100644 src/desktopexecentrytokenizer.cpp create mode 100644 tests/test_desktopexecentrytokenizer.cpp diff --git a/include/linuxdeploy/desktopfile/desktopexecentrytokenizer.h b/include/linuxdeploy/desktopfile/desktopexecentrytokenizer.h new file mode 100644 index 0000000..78057d0 --- /dev/null +++ b/include/linuxdeploy/desktopfile/desktopexecentrytokenizer.h @@ -0,0 +1,44 @@ +#pragma once + +// system +#include +#include + + +namespace linuxdeploy { + namespace desktopfile { + /** + * Allow to iterate over the Exec Desktop Entry Value respecting quoted sections according to + * https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s07.html + */ + class DesktopExecEntryTokenizer { + public: + explicit DesktopExecEntryTokenizer(const std::string& value); + + /** + * Move to the next section. + * @return true if a new section was found, false if the end of the string was reached + */ + bool next(); + + /** + * @return current section value without quotes + */ + std::string section() const; + + /** + * @return current section start position + */ + int sectionBegin() const; + + /** + * @return current section size + */ + int sectionSize() const; + + private: + struct Priv; + std::shared_ptr d_ptr; + }; + } +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f6d2af5..b0c9949 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(_linuxdeploy_desktopfile_objs OBJECT desktopfilereader.h desktopfilewriter.cpp desktopfilewriter.h + desktopexecentrytokenizer.cpp util.h ${HEADERS} ) diff --git a/src/desktopexecentrytokenizer.cpp b/src/desktopexecentrytokenizer.cpp new file mode 100644 index 0000000..81a87ac --- /dev/null +++ b/src/desktopexecentrytokenizer.cpp @@ -0,0 +1,86 @@ +// system +#include + +// local +#include "linuxdeploy/desktopfile/desktopexecentrytokenizer.h" + +namespace linuxdeploy { + namespace desktopfile { + struct DesktopExecEntryTokenizer::Priv { + std::string value; + unsigned long begin = 0; + unsigned long size = 0; + + std::string section; + }; + + DesktopExecEntryTokenizer::DesktopExecEntryTokenizer(const std::string& value) : d_ptr(new Priv()) { + d_ptr->value = value; + } + + bool DesktopExecEntryTokenizer::next() { + if ((d_ptr->begin + d_ptr->size) >= d_ptr->value.size()) + return false; + + // entries sections might delimited by a quote char (") or by blank spaces + + bool sectionCompleted = false; + bool quotedSection = false; + bool escapedChar = false; + unsigned long i = d_ptr->begin + d_ptr->size; + + // ignore white spaces at the begin + while (i < d_ptr->value.size() && d_ptr->value[i] == ' ') + i++; + + d_ptr->begin = i; + std::stringstream newSection; + + for (; i < d_ptr->value.size() && !sectionCompleted; i++) { + const char& c = d_ptr->value[i]; + + if (escapedChar) { + escapedChar = false; + newSection << c; + } else { + // quoted section begin or end + if (c == '\"' && !escapedChar) + quotedSection = !quotedSection; + + if (c == '\\') + escapedChar = true; + + // a blank space points the end of a non quoted section + if (c == ' ' && !quotedSection) + sectionCompleted = true; + + // append char to new section + if (!sectionCompleted) + newSection << c; + } + } + +// update pointers + d_ptr->section = newSection.str(); + d_ptr->size = i - d_ptr->begin; + + // don't count the space in the section size + if (i < d_ptr->value.size()) + d_ptr->size--; + + return true; + } + + std::string DesktopExecEntryTokenizer::section() const { + return d_ptr->section; + } + + int DesktopExecEntryTokenizer::sectionBegin() const { + return d_ptr->begin; + } + + int DesktopExecEntryTokenizer::sectionSize() const { + return d_ptr->size; + } + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8958c29..8a78e2a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(test_desktopfile test_desktopfilereader.cpp test_desktopfilewriter.cpp test_desktopfile_conformance.cpp + test_desktopexecentrytokenizer.cpp main.cpp ) diff --git a/tests/test_desktopexecentrytokenizer.cpp b/tests/test_desktopexecentrytokenizer.cpp new file mode 100644 index 0000000..0cbc6ee --- /dev/null +++ b/tests/test_desktopexecentrytokenizer.cpp @@ -0,0 +1,58 @@ +// system +#include + +// library headers +#include + + +// local +#include "linuxdeploy/desktopfile/desktopexecentrytokenizer.h" + + +using namespace linuxdeploy::desktopfile; + +TEST(DesktopExecEntryTokenizerTest, iterateSingleSectionEntry) { + DesktopExecEntryTokenizer tokenizer("echo"); + ASSERT_TRUE(tokenizer.next()); +} + +TEST(DesktopExecEntryTokenizerTest, iterateSimpleEntry) { + DesktopExecEntryTokenizer tokenizer("echo %F"); + ASSERT_TRUE(tokenizer.next()); + + ASSERT_EQ(tokenizer.section(), "echo"); + ASSERT_EQ(tokenizer.sectionBegin(), 0); + ASSERT_EQ(tokenizer.sectionSize(), 4); + + ASSERT_TRUE(tokenizer.next()); + + ASSERT_EQ(tokenizer.section(), "%F"); + ASSERT_EQ(tokenizer.sectionBegin(), 5); + ASSERT_EQ(tokenizer.sectionSize(), 2); + + ASSERT_FALSE(tokenizer.next()); +} + +TEST(DesktopExecEntryTokenizerTest, iterateQuotedEntry) { + DesktopExecEntryTokenizer tokenizer("\"/opt/custom apps/app\" %F --force=\"Rouge \\$1\""); + + ASSERT_TRUE(tokenizer.next()); + + ASSERT_EQ(tokenizer.section(), "\"/opt/custom apps/app\""); + ASSERT_EQ(tokenizer.sectionBegin(), 0); + ASSERT_EQ(tokenizer.sectionSize(), 22); + + ASSERT_TRUE(tokenizer.next()); + + ASSERT_EQ(tokenizer.section(), "%F"); + ASSERT_EQ(tokenizer.sectionBegin(), 23); + ASSERT_EQ(tokenizer.sectionSize(), 2); + + ASSERT_TRUE(tokenizer.next()); + + ASSERT_EQ(tokenizer.section(), "--force=\"Rouge \\$1\""); + ASSERT_EQ(tokenizer.sectionBegin(), 26); + ASSERT_EQ(tokenizer.sectionSize(), 19); + + ASSERT_FALSE(tokenizer.next()); +}