[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Rule Names



Hey!

So I've done some work with rule names / deleting individual rules.
The patchset i've attached is fully functioning as implimented. Feel free to discuss the
    implementation though: :)

Rule names themselves are just a single member of the rule struct now, implimented as a string.
There is no distintion between rule 'indexes' and 'names'. Rules that are not named are given
    a name that represents the index of the rule. But this is still the string. It is sprinted
    in (don't worry, it should be safe the way I did it). The index itself is a global unsigned
    long long, and is incremented with each rule being created, and is reset on 'unrule --all'
'unrule' searches using strcmp incrementing through the rule queue. It seems like this would be
    slow (its not really noticible) but I didn't know of a better way.
Add command 'list_rules', including rule names.

Tylo
>From 54c22202478b747e3591f15d8124a593cbcb2d13 Mon Sep 17 00:00:00 2001
From: Tyler Thomas Hart <htyler _at_ pdx _dot_ edu>
Date: Sat, 15 Dec 2012 15:11:50 -0800
Subject: [PATCH 1/3] Adds initial support for rule names.

A new char* member in the HSRule struct is added for names. New
property for rule command 'name=' added, to add custom names. New
rules without an explicit name is given an integer name. Adds flag
printname which prints the name of a newly created rule to stdout.
---
 NEWS                 |  2 ++
 doc/herbstluftwm.txt | 19 ++++++++++++++-----
 src/rules.c          | 26 ++++++++++++++++++++++++++
 src/rules.h          |  1 +
 4 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index 3680770..fe2a5af 100644
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,8 @@ Changes:
     * new commands: focus_edge, shift_edge
     * new command: shift_to_monitor
     * optional names for monitors, new command rename_monitor
+    * rule names: rules can be given names with the 'name' property. The name
+      can be printed to stdout using the 'printname' flag to the rule command.
 
 Release: 0.4.1 on 2012-08-30
 ----------------------------
diff --git a/doc/herbstluftwm.txt b/doc/herbstluftwm.txt
index c18af23..a9da8ca 100644
--- a/doc/herbstluftwm.txt
+++ b/doc/herbstluftwm.txt
@@ -641,7 +641,7 @@ floating [['TAG'] *on*|*off*|*toggle*|*status*]::
     is given, floating mode is toggled. If status is given, then *on* or *off*
     is printed, depending of the floating state on 'TAG'.
 
-rule [\[--]'FLAG'|\[--]'CONDITION'|\[--]'CONSEQUENCE' ...]::
+rule [\[--]'FLAG'|\[--]'NAME'|\[--]'CONDITION'|\[--]'CONSEQUENCE' ...]::
     Defines a rule which will be applied to all new clients. Its behaviour is
     described in the <<RULES,*RULES section*>>.
 
@@ -858,11 +858,19 @@ appear. Each rule matches against a certain subset of all clients and defines a
 set of properties for them (called 'CONSEQUENCE'##s##). A rule can be defined
 with this command:
 
-+rule+ [\[--]'FLAG'|\[--]'CONDITION'|\[--]'CONSEQUENCE' ...]
++rule+ [\[--]'FLAG'|\[--]'NAME'|\[--]'CONDITION'|\[--]'CONSEQUENCE' ...]
 
-Each rule consists of a list of 'FLAG'##s##, 'CONDITION'##s## and
-'CONSEQUENCE'##s## (each of them can be optionally prefixed with two dashes
-(+--+) to provide a more *iptables*(8)-like feeling).
+Each rule consists of a list of 'FLAG'##s##, 'CONDITION'##s## 'CONSEQUENCE'##s##
+and, optionally, a 'NAME'. (each of them can be optionally prefixed with two
+dashes (+--+) to provide a more *iptables*(8)-like feeling).
+
+Each rule can be given a custom name by specifying the 'NAME' property:
+
+    * +NAME+='VALUE'
+
+If multiple name properties are specified, the last one in the list will be
+applied. If no name is given, then the rule will be given a name that is an
+integer, representing the index of the rule since the first rule was applied.
 
 If a new client
 appears, herbstluftwm tries to apply each rule to this new client as follows:
@@ -952,6 +960,7 @@ A rule's behaviour can be configured by some special 'FLAGS':
     * +not+: negates the next 'CONDITION'.
     * +!+: same as +not+.
     * +once+: only apply this rule once (and delete it afterwards).
+    * +printname+: prints the name of the newly created rule to stdout.
 
 Examples:
 
diff --git a/src/rules.c b/src/rules.c
index 95a0ac5..6a85d5d 100644
--- a/src/rules.c
+++ b/src/rules.c
@@ -75,6 +75,7 @@ static HSConditionType g_condition_types[] = {
 
 int     g_maxage_type; // index of "maxage"
 time_t  g_current_rule_birth_time; // data from rules_apply() to condition_maxage()
+unsigned long long g_rule_id_index; // incremental index of rule ID
 
 static HSConsequenceType g_consequence_types[] = {
     { "tag",            consequence_tag             },
@@ -94,11 +95,13 @@ GQueue g_rules = G_QUEUE_INIT; // a list of HSRule* elements
 // RULES //
 void rules_init() {
     g_maxage_type = find_condition_type("maxage");
+    g_rule_id_index = 0ULL;
 }
 
 void rules_destroy() {
     g_queue_foreach(&g_rules, (GFunc)rule_destroy, NULL);
     g_queue_clear(&g_rules);
+    g_rule_id_index = 0ULL;
 }
 
 // condition types //
@@ -233,6 +236,10 @@ HSRule* rule_create() {
     HSRule* rule = g_new0(HSRule, 1);
     rule->once = false;
     rule->birth_time = get_monotonic_timestamp();
+    // Add name. Defaults to index number.
+    char name[22]; // 20 is the max decimal digits a u-l-l can be.
+    sprintf(name, "%llu", g_rule_id_index++);
+    rule->name = g_strdup(name);
     return rule;
 }
 
@@ -247,6 +254,8 @@ void rule_destroy(HSRule* rule) {
         consequence_destroy(rule->consequences[i]);
     }
     g_free(rule->consequences);
+    // free name
+    g_free(rule->name);
     // free rule itself
     g_free(rule);
 }
@@ -301,6 +310,7 @@ int rule_add_command(int argc, char** argv, GString* output) {
     HSRule* rule = rule_create();
     HSCondition* cond;
     HSConsequence* cons;
+    bool printname = false;
     bool negated = false;
     struct {
         char* name;
@@ -309,6 +319,7 @@ int rule_add_command(int argc, char** argv, GString* output) {
         { "not",    &negated },
         { "!",      &negated },
         { "once",   &rule->once },
+        { "printname",&printname },
     };
 
     while (SHIFT(argc, argv)) {
@@ -354,6 +365,17 @@ int rule_add_command(int argc, char** argv, GString* output) {
             rule_add_consequence(rule, cons);
         }
 
+        // Check for a provided name, and replace index if so
+        else if (consorcond && (!strcmp(name,"name"))) {
+            if (value[0] == '-') {
+                g_string_append_printf(output, "rule: name cannot begin with '-' (dash)");
+                return HERBST_INVALID_ARGUMENT;
+            } else {
+                g_free(rule->name);
+                rule->name = g_strdup(value);
+            }
+        }
+
         else {
             // need to hardcode "rule:" here because args are shifted
             g_string_append_printf(output,
@@ -362,6 +384,10 @@ int rule_add_command(int argc, char** argv, GString* output) {
         }
     }
 
+    if (printname) {
+       g_string_append_printf(output, "%s", rule->name);
+    }
+
     g_queue_push_tail(&g_rules, rule);
     return 0;
 }
diff --git a/src/rules.h b/src/rules.h
index 741ff4b..70f3612 100644
--- a/src/rules.h
+++ b/src/rules.h
@@ -43,6 +43,7 @@ typedef struct {
 } HSConsequence;
 
 typedef struct {
+    char*           name;
     HSCondition**   conditions;
     int             condition_count;
     HSConsequence** consequences;
-- 
1.8.0.2

>From a0eb42df2d5e5c3c47f8dd9983d9787b021e6cf5 Mon Sep 17 00:00:00 2001
From: Tyler Thomas Hart <htyler _at_ pdx _dot_ edu>
Date: Sat, 15 Dec 2012 15:49:14 -0800
Subject: [PATCH 2/3] Allows unrule command to remove by name

---
 NEWS                 |  1 +
 doc/herbstluftwm.txt |  6 +++---
 src/rules.c          | 35 ++++++++++++++++++++++++++++++++---
 3 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/NEWS b/NEWS
index fe2a5af..980abb7 100644
--- a/NEWS
+++ b/NEWS
@@ -30,6 +30,7 @@ Changes:
     * optional names for monitors, new command rename_monitor
     * rule names: rules can be given names with the 'name' property. The name
       can be printed to stdout using the 'printname' flag to the rule command.
+      Unrule command accepts names, and can remove rules individually.
 
 Release: 0.4.1 on 2012-08-30
 ----------------------------
diff --git a/doc/herbstluftwm.txt b/doc/herbstluftwm.txt
index a9da8ca..0d8efeb 100644
--- a/doc/herbstluftwm.txt
+++ b/doc/herbstluftwm.txt
@@ -645,9 +645,9 @@ rule [\[--]'FLAG'|\[--]'NAME'|\[--]'CONDITION'|\[--]'CONSEQUENCE' ...]::
     Defines a rule which will be applied to all new clients. Its behaviour is
     described in the <<RULES,*RULES section*>>.
 
-unrule *--all*|*-F*::
-    If --all or -F is passed, then all rules are removed.
-    (It is not possible yet to remove certain rules)
+unrule 'NAME'|*--all*|*-F*::
+    Removes all rules named 'NAME'. If --all or -F is passed, then all rules are
+    removed.
 
 fullscreen [*on*|*off*|*toggle*]::
     Sets or toggles the fullscreen state of the focused client. If no argument
diff --git a/src/rules.c b/src/rules.c
index 6a85d5d..1ad67e8 100644
--- a/src/rules.c
+++ b/src/rules.c
@@ -260,6 +260,31 @@ void rule_destroy(HSRule* rule) {
     g_free(rule);
 }
 
+// Compares the name of two rules.
+static gint rule_compare_name(const HSRule* a, const HSRule* b) {
+    return (!strcmp(a->name, b->name)) ? 0 : -1;
+}
+
+// Looks up a rule with a given index and removes if from the queue
+bool rule_find_pop(char* name) {
+    GList* rule = { NULL };
+    bool status = false; // Will be returned as true if any is found
+    HSRule rule_find = { .name = name };
+    while ((rule = g_queue_find_custom(&g_rules, &rule_find,
+                        (GCompareFunc)rule_compare_name))) {
+        // Check if rule with name exists
+        if ( rule == NULL ) {
+            break;
+        }
+        status = true;
+        // If so, clear data
+        rule_destroy(rule->data);
+        // Remove and free empty link
+        g_queue_delete_link(&g_rules, rule);
+    }
+    return status;
+}
+
 // parses an arg like NAME=VALUE to res_name, res_operation and res_value
 bool tokenize_arg(char* condition,
                   char** res_name, char* res_operation, char** res_value) {
@@ -401,12 +426,16 @@ int rule_remove_command(int argc, char** argv, GString* output) {
         // remove all rules
         g_queue_foreach(&g_rules, (GFunc)rule_destroy, NULL);
         g_queue_clear(&g_rules);
+        g_rule_id_index = 0ULL;
         return 0;
     }
 
-    g_string_append_printf(output,
-        "%s: This command must be used with --all/-F\n", argv[0]);
-    return HERBST_INVALID_ARGUMENT;
+    // Deletes rule with given name
+    if (!rule_find_pop(argv[1])) {
+        g_string_append_printf(output, "Couldn't find rule: %s", argv[1]);
+        return HERBST_INVALID_ARGUMENT;
+    }
+    return 0;
 }
 
 // rules applying //
-- 
1.8.0.2

>From 3c15d5c991fccfd25c798cf04b3e68818ff604bb Mon Sep 17 00:00:00 2001
From: Tyler Thomas Hart <htyler _at_ pdx _dot_ edu>
Date: Sat, 15 Dec 2012 16:16:28 -0800
Subject: [PATCH 3/3] Add command list_rules

---
 NEWS                 |  1 +
 doc/herbstluftwm.txt |  3 +++
 src/command.c        |  1 +
 src/main.c           |  1 +
 src/rules.c          | 47 +++++++++++++++++++++++++++++++++++++++++++----
 src/rules.h          |  6 +++++-
 6 files changed, 54 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index 980abb7..21e4ec7 100644
--- a/NEWS
+++ b/NEWS
@@ -31,6 +31,7 @@ Changes:
     * rule names: rules can be given names with the 'name' property. The name
       can be printed to stdout using the 'printname' flag to the rule command.
       Unrule command accepts names, and can remove rules individually.
+    * new command: list_rules
 
 Release: 0.4.1 on 2012-08-30
 ----------------------------
diff --git a/doc/herbstluftwm.txt b/doc/herbstluftwm.txt
index 0d8efeb..54b7fac 100644
--- a/doc/herbstluftwm.txt
+++ b/doc/herbstluftwm.txt
@@ -153,6 +153,9 @@ list_commands::
     List currently configured monitors with their index, area (as rectangle),
     name (if named) and currently viewed tag.
 
+list_rules::
+    Lists all active rules with their associated names.
+
 list_keybinds::
     Lists all bound keys with their associated command. Each line consists of
     one key combination and the command with its parameters separated by tabs.
diff --git a/src/command.c b/src/command.c
index 4cb8c91..a8b2afe 100644
--- a/src/command.c
+++ b/src/command.c
@@ -58,6 +58,7 @@ struct {
     { "list_commands",  1,  no_completion },
     { "list_monitors",  1,  no_completion },
     { "list_keybinds",  1,  no_completion },
+    { "list_rules",     1,  no_completion },
     { "lock",           1,  no_completion },
     { "unlock",         1,  no_completion },
     { "keybind",        2,  keybind_parameter_expected },
diff --git a/src/main.c b/src/main.c
index 6cf5606..ece5a8c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -144,6 +144,7 @@ CommandBinding g_commands[] = {
     CMD_BIND(             "raise",          raise_command),
     CMD_BIND(             "rule",           rule_add_command),
     CMD_BIND(             "unrule",         rule_remove_command),
+    CMD_BIND(             "list_rules",     rule_print_all_command),
     CMD_BIND(             "layout",         print_layout_command),
     CMD_BIND(             "stack",          print_stack_command),
     CMD_BIND(             "dump",           print_layout_command),
diff --git a/src/rules.c b/src/rules.c
index 1ad67e8..c1b97a3 100644
--- a/src/rules.c
+++ b/src/rules.c
@@ -141,15 +141,16 @@ HSCondition* condition_create(int type, char op, char* value, GString* output) {
 
         case '~':
             cond.value_type = CONDITION_VALUE_TYPE_REGEX;
-            int status = regcomp(&cond.value.exp, value, REG_EXTENDED);
+            int status = regcomp(&cond.value.reg.exp, value, REG_EXTENDED);
             if (status != 0) {
                 char buf[ERROR_STRING_BUF_SIZE];
-                regerror(status, &cond.value.exp, buf, ERROR_STRING_BUF_SIZE);
+                regerror(status, &cond.value.reg.exp, buf, ERROR_STRING_BUF_SIZE);
                 g_string_append_printf(output,
                     "rule: Can not parse value \"%s\" from condition \"%s\": \"%s\"\n",
                     value, g_condition_types[type].name, buf);
                 return NULL;
             }
+            cond.value.reg.str = g_strdup(value);
             break;
 
         default:
@@ -176,7 +177,8 @@ void condition_destroy(HSCondition* cond) {
             free(cond->value.str);
             break;
         case CONDITION_VALUE_TYPE_REGEX:
-            regfree(&cond->value.exp);
+            regfree(&cond->value.reg.exp);
+            g_free(cond->value.reg.str);
             break;
         default:
             break;
@@ -285,6 +287,43 @@ bool rule_find_pop(char* name) {
     return status;
 }
 
+// List all rules in queue
+static void rule_print_append_output(HSRule* rule, GString* output) {
+    g_string_append_printf(output, "[%s]: ", rule->name);
+    // Append conditions
+    for (int i = 0; i < rule->condition_count; i++) {
+        g_string_append_printf(output, "%s=",
+                               g_condition_types[rule->conditions[i]->condition_type].name);
+        if (rule->conditions[i]->value_type == CONDITION_VALUE_TYPE_STRING) {
+            g_string_append_printf(output, "%s ",
+                                   rule->conditions[i]->value.str);
+        } else if (rule->conditions[i]->value_type == CONDITION_VALUE_TYPE_REGEX) {
+            g_string_append_printf(output, "%s ",
+                                   rule->conditions[i]->value.reg.str);
+        } else if (rule->conditions[i]->value_type == CONDITION_VALUE_TYPE_INTEGER) {
+            g_string_append_printf(output, "%i ",
+                                   rule->conditions[i]->value.integer);
+        }
+    }
+    // Append consequences
+    for (int i = 0; i < rule->consequence_count; i++) {
+        g_string_append_printf(output, "%s=",
+                               g_consequence_types[rule->consequences[i]->type].name);
+        if (rule->consequences[i]->value_type == CONSEQUENCE_VALUE_TYPE_STRING) {
+            g_string_append_printf(output, "%s ",
+                                   rule->consequences[i]->value.str);
+        }
+    }
+    // Print new line
+    g_string_append_printf(output, "\n");
+}
+
+int rule_print_all_command(int argc, char** argv, GString* output) {
+    // Print entry for each in the queue
+    g_queue_foreach(&g_rules, (GFunc)rule_print_append_output, output);
+    return 0;
+}
+
 // parses an arg like NAME=VALUE to res_name, res_operation and res_value
 bool tokenize_arg(char* condition,
                   char** res_name, char* res_operation, char** res_value) {
@@ -532,7 +571,7 @@ bool condition_string(HSCondition* rule, char* string) {
             return !strcmp(string, rule->value.str);
             break;
         case CONDITION_VALUE_TYPE_REGEX:
-            status = regexec(&rule->value.exp, string, 1, &match, 0);
+            status = regexec(&rule->value.reg.exp, string, 1, &match, 0);
             // only accept it, if it matches the entire string
             if (status == 0
                 && match.rm_so == 0
diff --git a/src/rules.h b/src/rules.h
index 70f3612..1f2f7e2 100644
--- a/src/rules.h
+++ b/src/rules.h
@@ -29,7 +29,10 @@ typedef struct {
     bool negated;
     union {
         char*       str;
-        regex_t     exp;
+        struct {
+            regex_t     exp;
+            char*       str;
+        } reg;
         int         integer;
     } value;
 } HSCondition;
@@ -74,6 +77,7 @@ void rule_destroy(HSRule* rule);
 
 int rule_add_command(int argc, char** argv, GString* output);
 int rule_remove_command(int argc, char** argv, GString* output);
+int rule_print_all_command(int argc, char** argv, GString* output);
 
 #endif
 
-- 
1.8.0.2