From 7ef02a3b09be621a6dc16a1b1d3abcf4e1a8a8b0 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Tue, 5 Apr 2022 08:48:12 +0200 Subject: [PATCH] Allow having semi-colons in quoted arguments in the cmd part of mimeview defs --- src/common/rclconfig.cpp | 14 +- src/common/rclconfig.h | 14 +- src/testmains/trrclconfig.cpp | 243 ++++++++++++++++++++-------------- 3 files changed, 162 insertions(+), 109 deletions(-) diff --git a/src/common/rclconfig.cpp b/src/common/rclconfig.cpp index 490d30ea..15d8fe86 100644 --- a/src/common/rclconfig.cpp +++ b/src/common/rclconfig.cpp @@ -905,11 +905,17 @@ bool RclConfig::getGuiFilter(const string& catfiltername, string& frag) const return true; } -bool RclConfig::valueSplitAttributes(const string& whole, string& value, - ConfSimple& attrs) +bool RclConfig::valueSplitAttributes(const string& whole, string& value, ConfSimple& attrs) { - /* There is currently no way to escape a semi-colon */ - string::size_type semicol0 = whole.find_first_of(";"); + bool inquote{false}; + string::size_type semicol0; + for (semicol0 = 0; semicol0 < whole.size(); semicol0++) { + if (whole[semicol0] == '"') { + inquote = !inquote; + } else if (whole[semicol0] == ';' && !inquote) { + break; + } + } value = whole.substr(0, semicol0); trimstring(value); string attrstr; diff --git a/src/common/rclconfig.h b/src/common/rclconfig.h index a528bc75..32437ad6 100644 --- a/src/common/rclconfig.h +++ b/src/common/rclconfig.h @@ -259,12 +259,18 @@ public: string getMimeHandlerDef(const string &mimetype, bool filtertypes=false, const std::string& fn = std::string()); - /** For lines like: "name = some value; attr1 = value1; attr2 = val2" + /** For lines like: [name = some value; attr1 = value1; attr2 = val2] * Separate the value and store the attributes in a ConfSimple - * @param whole the raw value. No way to escape a semi-colon in there. + * + * In the value part, semi-colons inside double quotes are ignored, and double quotes are + * conserved. In the common case where the string is then processed by stringToStrings() to + * build a command line, this allows having semi-colons inside arguments. However, no backslash + * escaping is possible, so that, for example "bla\"1;2\"" would not work (the value part + * would stop at the semi-colon). + * + * @param whole the raw value. */ - static bool valueSplitAttributes(const string& whole, string& value, - ConfSimple& attrs) ; + static bool valueSplitAttributes(const string& whole, string& value, ConfSimple& attrs) ; /** Compute difference between 'base' and 'changed', as elements to be * added and substracted from base. Input and output strings are in diff --git a/src/testmains/trrclconfig.cpp b/src/testmains/trrclconfig.cpp index c9d01696..9a1f76e5 100644 --- a/src/testmains/trrclconfig.cpp +++ b/src/testmains/trrclconfig.cpp @@ -32,19 +32,111 @@ using namespace std; #include "rclconfig.h" #include "cstr.h" +void showFields(RclConfig *config) +{ + set stored = config->getStoredFields(); + set indexed = config->getIndexedFields(); + cout << "Stored fields: "; + for (const auto& field: stored) { + cout << "[" << field << "] "; + } + cout << "\n"; + cout << "Indexed fields: "; + for (const auto& field : indexed) { + const FieldTraits *ftp; + config->getFieldTraits(field, &ftp); + if (ftp) + cout << "[" << field << "]" << " -> [" << ftp->pfx << "] "; + else + cout << "[" << field << "]" << " -> [" << "(none)" << "] "; + + } + cout << "\n"; +} + +void checkMtypesConsistency(RclConfig *config) +{ + // Checking the configuration consistency + + // Find and display category names + vector catnames; + config->getMimeCategories(catnames); + cout << "Categories: "; + for (const auto& catg : catnames) { + cout << catg << " "; + } + cout << "\n"; + + // Compute union of all types from each category. Check that there + // are no duplicates while we are at it. + set allmtsfromcats; + for (const auto& catg : catnames) { + vector cts; + config->getMimeCatTypes(catg, cts); + for (const auto& cattype : cts) { + // Already in map -> duplicate + if (allmtsfromcats.find(cattype) != allmtsfromcats.end()) { + cout << "Duplicate: [" << cattype << "]" << "\n"; + } + allmtsfromcats.insert(cattype); + } + } + + // Retrieve complete list of mime types + vector mtypes = config->getAllMimeTypes(); + + // And check that each mime type is found in exactly one category + // And have an icon + for (const auto& mtype: mtypes) { + if (allmtsfromcats.find(mtype) == allmtsfromcats.end()) { + cout << "Not found in catgs: [" << mtype << "]" << "\n"; + } + // We'd like to check for types with no icons, but + // getMimeIconPath() returns the valid 'document' by + // default, we'd have to go look into the confsimple + // directly. + // string path = config->getMimeIconPath(mtype, string()); + // cout << mtype << " -> " << path << "\n"; + } + + // List mime types not in mimeview + for (const auto & mtype : mtypes) { + if (config->getMimeViewerDef(mtype, "", false).empty()) { + cout << "No viewer: [" << mtype << "]" << "\n"; + } + } + + // Check that each mime type has an indexer + for (const auto & mtype : mtypes) { + if (config->getMimeHandlerDef(mtype, false).empty()) { + cout << "No filter: [" << mtype << "]" << "\n"; + } + } + + // Check that each mime type has a defined icon + for (const auto & mtype : mtypes) { + if (config->getMimeIconPath(mtype, "") == "document") { + cout << "No or generic icon: [" << mtype << "]" << "\n"; + } + } +} + + + static char *thisprog; static char usage [] = "\n" + " -h : print help\n" "-c: check a few things in the configuration files\n" "[-s subkey] -q param : query parameter value\n" "-f : print some field data\n" - " : default: print parameters\n" - + "-l : list recoll.conf parameters\n" + " : default: nothing\n" ; static void -Usage(void) +Usage(FILE *fp = stderr) { - fprintf(stderr, "%s: usage: %s\n", thisprog, usage); + fprintf(fp, "%s: usage: %s\n", thisprog, usage); exit(1); } @@ -54,6 +146,8 @@ static int op_flags; #define OPT_q 0x4 #define OPT_c 0x8 #define OPT_f 0x10 +#define OPT_l 0x20 +#define OPT_h 0x40 int main(int argc, char **argv) { @@ -71,6 +165,10 @@ int main(int argc, char **argv) switch (*(*argv)++) { case 'c': op_flags |= OPT_c; break; case 'f': op_flags |= OPT_f; break; + case 'h': op_flags |= OPT_h; + Usage(stdout); + break; + case 'l': op_flags |= OPT_l; break; case 's': op_flags |= OPT_s; if (argc < 2) Usage(); skey = *(++argv); argc--; @@ -90,7 +188,7 @@ int main(int argc, char **argv) string reason; RclConfig *config = recollinit(0, 0, 0, reason); if (config == 0 || !config->ok()) { - cerr << "Configuration problem: " << reason << endl; + cerr << "Configuration problem: " << reason << "\n"; exit(1); } if (op_flags & OPT_s) @@ -103,106 +201,49 @@ int main(int argc, char **argv) } printf("[%s] -> [%s]\n", pname.c_str(), value.c_str()); } else if (op_flags & OPT_f) { - set stored = config->getStoredFields(); - set indexed = config->getIndexedFields(); - cout << "Stored fields: "; - for (set::const_iterator it = stored.begin(); - it != stored.end(); it++) { - cout << "[" << *it << "] "; - } - cout << endl; - cout << "Indexed fields: "; - for (set::const_iterator it = indexed.begin(); - it != indexed.end(); it++) { - const FieldTraits *ftp; - config->getFieldTraits(*it, &ftp); - if (ftp) - cout << "[" << *it << "]" << " -> [" << ftp->pfx << "] "; - else - cout << "[" << *it << "]" << " -> [" << "(none)" << "] "; - - } - cout << endl; + showFields(config); } else if (op_flags & OPT_c) { - // Checking the configuration consistency - - // Find and display category names - vector catnames; - config->getMimeCategories(catnames); - cout << "Categories: "; - for (vector::const_iterator it = catnames.begin(); - it != catnames.end(); it++) { - cout << *it << " "; - } - cout << endl; - - // Compute union of all types from each category. Check that there - // are no duplicates while we are at it. - set allmtsfromcats; - for (vector::const_iterator it = catnames.begin(); - it != catnames.end(); it++) { - vector cts; - config->getMimeCatTypes(*it, cts); - for (vector::const_iterator it1 = cts.begin(); - it1 != cts.end(); it1++) { - // Already in map -> duplicate - if (allmtsfromcats.find(*it1) != allmtsfromcats.end()) { - cout << "Duplicate: [" << *it1 << "]" << endl; - } - allmtsfromcats.insert(*it1); - } - } - - // Retrieve complete list of mime types - vector mtypes = config->getAllMimeTypes(); - - // And check that each mime type is found in exactly one category - // And have an icon - for (const auto& mtype: mtypes) { - if (allmtsfromcats.find(mtype) == allmtsfromcats.end()) { - cout << "Not found in catgs: [" << mtype << "]" << endl; - } - // We'd like to check for types with no icons, but - // getMimeIconPath() returns the valid 'document' by - // default, we'd have to go look into the confsimple - // directly. - // string path = config->getMimeIconPath(mtype, string()); - // cout << mtype << " -> " << path << endl; - } - - // List mime types not in mimeview - for (vector::const_iterator it = mtypes.begin(); - it != mtypes.end(); it++) { - if (config->getMimeViewerDef(*it, "", false).empty()) { - cout << "No viewer: [" << *it << "]" << endl; - } - } - - // Check that each mime type has an indexer - for (vector::const_iterator it = mtypes.begin(); - it != mtypes.end(); it++) { - if (config->getMimeHandlerDef(*it, false).empty()) { - cout << "No filter: [" << *it << "]" << endl; - } - } - - // Check that each mime type has a defined icon - for (vector::const_iterator it = mtypes.begin(); - it != mtypes.end(); it++) { - if (config->getMimeIconPath(*it, "") == "document") { - cout << "No or generic icon: [" << *it << "]" << endl; - } - } - - } else { + checkMtypesConsistency(config); + } else if (op_flags & OPT_l) { config->setKeyDir(cstr_null); vector names = config->getConfNames(); - for (vector::iterator it = names.begin(); - it != names.end();it++) { + for (const auto& name : names) { string value; - config->getConfParam(*it, value); - cout << *it << " -> [" << value << "]" << endl; + config->getConfParam(name, value); + cout << name << " -> [" << value << "]" << "\n"; + } + } else if (1) { + + std::vector samples { + "ebook-viewer %F;ignoreipath=1", + "rclshowinfo %F %(title);ignoreipath=1", + "xterm -u8 -e \"groff -T ascii -man %f | more\"", + "xterm -e \"info -f %f\"", + + "\"C:/Program Files/Tracker Software/PDF Editor/PDFXEdit.exe\" /A " + "\"page=%p;search=%s\" \"%f\"", + + "\"C:/Program Files/Tracker Software/PDF Editor/PDFXEdit.exe\" /A " + "\"page=%p;search=\\\"%s\\\"\" \"%f\"", + + "\"C:/Program Files/Tracker Software/PDF Editor/PDFXEdit.exe\" /A " + "\"page=%p;search=%s\" \"%f\"; someattr = somevalue;quotedattr = \"hello world\"", + }; + + for (const auto& sample: samples) { + string value; + ConfSimple attrs; + config->valueSplitAttributes(sample, value, attrs); + std::cout << "Input: ["<< sample << "]\n"; + std::cout << "Value: [" << value << "]\n"; + std::cout << "Attrs: "; + for (const auto& nm : attrs.getNames("")) { + std::string attr; + attrs.get(nm, attr); + std::cout << "{[" << nm << "] -> [" << attr << "]} "; + } + std::cout << "\n\n"; } } - exit(0); + return 0; }