changeset 675:168ea30142d9

Irccd: add color support in irccd output, closes #792 @1h
author David Demelier <markand@malikania.fr>
date Wed, 11 Apr 2018 22:21:21 +0200
parents 5d0ed41be10c
children 747665d5062b
files doc/src/irccd.conf.md doc/src/irccd.md libcommon/irccd/string_util.cpp libcommon/irccd/string_util.hpp libirccd/irccd/daemon/irccd.cpp
diffstat 5 files changed, 123 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/doc/src/irccd.conf.md	Wed Apr 11 21:01:10 2018 +0200
+++ b/doc/src/irccd.conf.md	Wed Apr 11 22:21:21 2018 +0200
@@ -127,7 +127,7 @@
 Only one keyword is defined, `message` which contains the message that irccd
 wants to output.
 
-**Note:** colors and attributes are not supported.
+**Note:** colors and attributes are not supported on Windows.
 
 The available options:
 
--- a/doc/src/irccd.md	Wed Apr 11 21:01:10 2018 +0200
+++ b/doc/src/irccd.md	Wed Apr 11 22:21:21 2018 +0200
@@ -96,7 +96,7 @@
 If supported, you can use environment variables like **${HOME}**. Please note
 that braces are mandatory.
 
-## Attributes
+## IRC attributes
 
 The attribute format is composed of three parts, foreground, background and
 modifiers, each separated by a comma.
@@ -135,6 +135,34 @@
   - underline2,
   - reverse.
 
+## Shell attributes
+
+Like IRC attributes, it's possible to specify colors and attributes in some
+places such as logger configuration.
+
+Warning: colors are not supported on all platforms.
+
+### Available colors
+
+  - black,
+  - red,
+  - green,
+  - orange,
+  - blue,
+  - purple,
+  - cyan,
+  - white,
+  - default.
+
+### Available attributes
+
+  - bold,
+  - dim,
+  - underline,
+  - blink,
+  - reverse,
+  - hidden.
+
 ## Keywords
 
 Keywords are arbitrary names that are replaced depending on the context. They
--- a/libcommon/irccd/string_util.cpp	Wed Apr 11 21:01:10 2018 +0200
+++ b/libcommon/irccd/string_util.cpp	Wed Apr 11 22:21:21 2018 +0200
@@ -38,7 +38,7 @@
 
 namespace {
 
-const std::unordered_map<std::string, int> colors{
+const std::unordered_map<std::string, int> irc_colors{
     { "white",      0   },
     { "black",      1   },
     { "blue",       2   },
@@ -57,7 +57,7 @@
     { "lightgrey",  15  }
 };
 
-const std::unordered_map<std::string, char> attributes{
+const std::unordered_map<std::string, char> irc_attributes{
     { "bold",       '\x02'  },
     { "italic",     '\x09'  },
     { "strike",     '\x13'  },
@@ -67,6 +67,27 @@
     { "reverse",    '\x16'  }
 };
 
+const std::unordered_map<std::string, unsigned> shell_colors{
+    { "black",      30  },
+    { "red",        31  },
+    { "green",      32  },
+    { "orange",     33  },
+    { "blue",       34  },
+    { "purple",     35  },
+    { "cyan",       36  },
+    { "white",      37  },
+    { "default",    39  },
+};
+
+const std::unordered_map<std::string, unsigned> shell_attributes{
+    { "bold",       1   },
+    { "dim",        2   },
+    { "underline",  4   },
+    { "blink",      5   },
+    { "reverse",    7   },
+    { "hidden",     8   }
+};
+
 inline bool is_reserved(char token) noexcept
 {
     return token == '#' || token == '@' || token == '$' || token == '!';
@@ -113,14 +134,15 @@
     return "";
 }
 
-std::string subst_attrs(const std::string& content)
+std::string subst_irc_attrs(const std::string& content)
 {
-    std::stringstream oss;
-    std::vector<std::string> list = split(content, ",");
+    auto list = split(content, ",");
 
     // @{} means reset.
     if (list.empty())
-        return std::string(1, attributes.at("reset"));
+        return std::string(1, irc_attributes.at("reset"));
+
+    std::ostringstream oss;
 
     // Remove useless spaces.
     std::transform(list.begin(), list.end(), list.begin(), strip);
@@ -136,19 +158,19 @@
         oss << '\x03';
 
         // Foreground.
-        auto it = colors.find(foreground);
-        if (it != colors.end())
+        auto it = irc_colors.find(foreground);
+        if (it != irc_colors.end())
             oss << it->second;
 
         // Background.
-        if (list.size() >= 2 && (it = colors.find(list[1])) != colors.end())
+        if (list.size() >= 2 && (it = irc_colors.find(list[1])) != irc_colors.end())
             oss << "," << it->second;
 
         // Attributes.
         for (std::size_t i = 2; i < list.size(); ++i) {
-            auto attribute = attributes.find(list[i]);
+            auto attribute = irc_attributes.find(list[i]);
 
-            if (attribute != attributes.end())
+            if (attribute != irc_attributes.end())
                 oss << attribute->second;
         }
     }
@@ -156,6 +178,60 @@
     return oss.str();
 }
 
+std::string subst_shell_attrs(const std::string& content)
+{
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+    auto list = split(content, ",");
+
+    if (list.empty())
+        return "";
+    if (list.size() > 3)
+        return "";
+
+    std::vector<std::string> seq;
+
+    /*
+     * Shell sequence looks like this:
+     *
+     * ttributes;foreground;backgroundm
+     */
+    if (list.size() >= 3) {
+        const auto it = shell_attributes.find(list[2]);
+
+        if (it != shell_attributes.end())
+            seq.push_back(std::to_string(it->second));
+        else
+            return "";
+    }
+    if (list.size() >= 1) {
+        const auto it = shell_colors.find(list[0]);
+
+        if (it != shell_colors.end())
+            seq.push_back(std::to_string(it->second));
+        else
+            return "";
+    }
+    if (list.size() >= 2) {
+        const auto it = shell_colors.find(list[1]);
+
+        if (it != shell_colors.end())
+            seq.push_back(std::to_string(it->second + 10));
+        else
+            return "";
+    }
+
+    std::ostringstream oss;
+
+    oss << "[";
+    oss << string_util::join(seq, ';');
+    oss << "m";
+
+    return oss.str();
+#else
+    return "";
+#endif
+}
+
 std::string subst_shell(const std::string& command)
 {
 #if defined(HAVE_POPEN)
@@ -218,7 +294,9 @@
         break;
     case '@':
         if ((params.flags & subst_flags::irc_attrs) == subst_flags::irc_attrs)
-            value = subst_attrs(content);
+            value = subst_irc_attrs(content);
+        else if ((params.flags & subst_flags::shell_attrs) == subst_flags::shell_attrs)
+            value = subst_shell_attrs(content);
         break;
     case '!':
         if ((params.flags & subst_flags::shell) == subst_flags::shell)
--- a/libcommon/irccd/string_util.hpp	Wed Apr 11 21:01:10 2018 +0200
+++ b/libcommon/irccd/string_util.hpp	Wed Apr 11 22:21:21 2018 +0200
@@ -52,7 +52,8 @@
     keywords    = (1 << 1),     //!< keywords
     env         = (1 << 2),     //!< environment variables
     shell       = (1 << 3),     //!< command line command
-    irc_attrs   = (1 << 4)      //!< IRC escape codes
+    irc_attrs   = (1 << 4),     //!< IRC escape codes
+    shell_attrs = (1 << 5)      //!< shell attributes
 };
 
 /**
--- a/libirccd/irccd/daemon/irccd.cpp	Wed Apr 11 21:01:10 2018 +0200
+++ b/libirccd/irccd/daemon/irccd.cpp	Wed Apr 11 22:21:21 2018 +0200
@@ -45,6 +45,7 @@
         string_util::subst params;
 
         params.flags &= ~(string_util::subst_flags::irc_attrs);
+        params.flags |= string_util::subst_flags::shell_attrs;
         params.keywords.emplace("message", std::move(input));
 
         return string_util::format(tmpl, params);