changeset 372:6a7850696b86

Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
author David Demelier <markand@malikania.fr>
date Thu, 08 Dec 2016 13:15:37 +0100
parents c6fb00344c19
children 2a9805acb178
files doc/html/irccdctl/configuring.md doc/man/irccdctl.conf.5.in irccdctl/main.cpp
diffstat 3 files changed, 115 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/doc/html/irccdctl/configuring.md	Wed Dec 07 12:33:32 2016 +0100
+++ b/doc/html/irccdctl/configuring.md	Thu Dec 08 13:15:37 2016 +0100
@@ -58,3 +58,53 @@
 type = "unix"
 path = "/tmp/irccd.sock"
 ````
+
+# The alias section
+
+The alias section can be used to define custom user commands.
+
+To define an alias, just add a new section named `[alias.name]` where name is
+your desired alias name.
+
+Then, add any option you like to execute commands you want. The option name is
+ignored and serves as auto-documentation only.
+
+Example:
+
+````ini
+[alias.present]
+say-hello = ( "server-message", "localhost", "#staff", "hello world!" )
+warning = ( "server-me", "localhost", "#staff", "is a bot")
+````
+
+This example defines an alias `present` that will:
+
+  1. Send a message on the channel #staff in the server localhost
+  2. Send an action emote on the same channel
+
+To use this alias, call `irccdctl present`.
+
+## Placeholders
+
+Sometimes, you want to pass parameters to your alias. The placeholder syntax
+allows you to define where your command line arguments will be replaced before
+being sent to irccd.
+
+The syntax uses `%n` where **n** is an integer starting from 0.
+
+As you have seen in the `present` alias example above, the channel and server
+are hardcoded so the user is not able to use this alias for different channels.
+Let's update this alias with placeholders to make it more generic.
+
+Example:
+
+````ini
+[alias.present]
+say-hello = ( "server-message", "%0", "%1", "hello world!" )
+warning = ( "server-me", "%0", "%1", "is a bot")
+````
+
+Now, the `present` alias will except two arguments from the command line when
+the user invokes `irccdctl present`. Thus if you want to use this alias on the
+**#staff@localhost**, you call the alias using
+`irccdctl present localhost #staff`
--- a/doc/man/irccdctl.conf.5.in	Wed Dec 07 12:33:32 2016 +0100
+++ b/doc/man/irccdctl.conf.5.in	Thu Dec 08 13:15:37 2016 +0100
@@ -85,6 +85,38 @@
 .It path
 (string) the file path to the socket.
 .El
+.\" ALIAS
+.Ss alias
+The alias section can be used to define custom user commands.
+.Pp
+To define an alias, just add a new section named
+.Nm alias.name
+where name is your desired alias name.
+.Pp
+Then, add any option you like to execute commands you want. The option name is
+ignored and serves as auto-documentation only.
+.Pp
+Example:
+.Bd -literal
+[alias.present]
+say-hello = ( "server-message", "localhost", "#staff", "hello world!" )
+warning = ( "server-me", "localhost", "#staff", "is a bot")
+.Ed
+.Pp
+.Nm Placeholders
+.Pp
+Sometimes, you want to pass parameters to your alias. The placeholder syntax
+allows you to define where your command line arguments will be replaced before
+being sent to irccd.
+.Pp
+The syntax uses `%n` where **n** is an integer starting from 0.
+.Pp
+Example:
+.Bd -literal
+[alias.present]
+say-hello = ( "server-message", "%0", "%1", "hello world!" )
+warning = ( "server-me", "%0", "%1", "is a bot")
+.Ed
 .\" FILES
 .Sh FILES
 The default config file is located at
--- a/irccdctl/main.cpp	Wed Dec 07 12:33:32 2016 +0100
+++ b/irccdctl/main.cpp	Thu Dec 08 13:15:37 2016 +0100
@@ -222,37 +222,39 @@
 }
 
 /*
- * readAliases
+ * readAlias
  * -------------------------------------------------------------------
  *
  * Read aliases for irccdctl.
  *
- * [alias]
- * name = ( "command", "arg1, "...", "argn" )
+ * [alias.<name>]
+ * cmd1 = ( "command", "arg1, "...", "argn" )
+ * cmd2 = ( "command", "arg1, "...", "argn" )
  */
-void readAliases(const ini::Section &sc)
+Alias readAlias(const ini::Section &sc, const std::string &name)
 {
-    for (const auto &option : sc) {
-        // This is the alias name.
-        Alias alias(option.key());
+    Alias alias(name);
 
-        // Iterate over the list of commands to execute for this alias.
-        for (const auto &repl : option) {
-            // This is the alias split string.
-            auto list = util::split(repl, " \t");
-
-            if (list.size() < 1)
-                throw std::invalid_argument("alias require at least one argument");
-
-            // First argument is the command/alias to execute.
-            auto command = list[0];
-
-            // Remove command name and puts arguments.
-            alias.push_back({std::move(command), std::vector<AliasArg>(list.begin() + 1, list.end())});
+    /*
+     * Each defined option is a command that the user can call. The name is
+     * unused and serves as documentation purpose.
+     */
+    for (const auto &option : sc) {
+        /*
+         * Iterate over the arguments which are usually a list and the first
+         * argument is a command name.
+         */
+        if (option.size() == 1 && option[0].empty()) {
+            throw std::runtime_error("alias {}: missing command name in '{}'"_format(name, option.key()));
         }
 
-        aliases.emplace(option.key(), std::move(alias));
+        std::string command = option[0];
+        std::vector<AliasArg> args(option.begin() + 1, option.end());
+
+        alias.emplace_back(std::move(command), std::move(args));
     }
+
+    return alias;
 }
 
 void read(const std::string &path)
@@ -265,8 +267,16 @@
             readConnect(*it);
         if ((it = doc.find("general")) != doc.end())
             readGeneral(*it);
-        if ((it = doc.find("alias")) != doc.end())
-            readAliases(*it);
+
+        // [alias.*] sections.
+        for (const auto& sc : doc) {
+            if (sc.key().compare(0, 6, "alias.") == 0) {
+                auto name = sc.key().substr(6);
+                auto alias = readAlias(sc, name);
+
+                aliases.emplace(std::move(name), std::move(alias));
+            }
+        }
     } catch (const std::exception &ex) {
         log::warning() << path << ": " << ex.what() << std::endl;
     }