]> git.mdlowis.com Git - projs/tide.git/commitdiff
fetch now supports a local rules file
authorMichael D. Lowis <mike@mdlowis.com>
Sat, 2 Nov 2019 03:56:34 +0000 (23:56 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Sat, 2 Nov 2019 03:56:34 +0000 (23:56 -0400)
TODO.md
config.h
src/fetch.c
src/tide.c

diff --git a/TODO.md b/TODO.md
index 6c6a3e03715255c26eb55bbca2265040408d1a60..19e4f125bc082e9e4de779952085064fec5b3a35 100644 (file)
--- a/TODO.md
+++ b/TODO.md
@@ -4,6 +4,7 @@
 
 ## STAGING
 
+* fetch: handle quotes around second arg
 * tide: move looping of cursor movements into selmoveby
 * all: eliminate multiple return statements and other lint
 * tide: byrune, byword, byline functions should be hidden in buf.c
index 1a940d0a79ad7945a9198a381180f73a94b2ff65..2a198cec0acee7bc6073701fdbee85819b8b004c 100644 (file)
--- a/config.h
+++ b/config.h
@@ -28,7 +28,7 @@ static char* ShellCmd[] = { 0, "-c", 0, 0 };
 static char* SedCmd[] = { "sed", "-Ee", 0, 0 };
 
 /* Command used to fetch some text based on a set of rules */
-static char* FetchCmd[] = { "fetch", "--", 0, 0 };
+static char* FetchCmd[] = { "fetch", 0, 0 };
 
 /* Default tag region text in editor windows */
 static char* TagString = "Del Put Undo Redo | Font Tabs Eol | Find ";
@@ -86,50 +86,50 @@ static int Palette[ClrCount] = {
     #define OPENCMD "xdg-open"
 #endif
 
-static Rule* BuiltinRules[] = {
-    (Rule[]){ /* Match URLS and open them with the browser */
-        { ISSET, "BROWSER", NULL },
-        { MATCHES, "data", "^(https?|ftp)://.*" },
-        { LAUNCH, "$BROWSER \"$M0\"", NULL },
-        { COMPLETE, NULL, NULL }
-    },
-    (Rule[]){ /* Open files with addresses in the editor */
-        { MATCHES, "data", "^([^:]+):([0-9]+)" },
-        { ISFILE, "$M1", NULL },
-        { LAUNCH, "edit \"$M0\"", NULL },
-        { COMPLETE, NULL, NULL }
-    },
-    (Rule[]){ /* Open addresses in the current file */
-        { MATCHES, "data", "^:?([0-9]+)" },
-        { ISFILE, "$file", NULL },
-        { LAUNCH, "edit \"$file:$M1\"", NULL },
-        { COMPLETE, NULL, NULL }
-    },
-    (Rule[]){ /* If it's an existing text file, open it with editor */
-        { ISFILE, "$data", NULL },
-        { EXEC, "file --mime \"$file\" | grep -q 'text/'", NULL },
-        { LAUNCH, "edit \"$file\"", NULL },
-        { COMPLETE, NULL, NULL }
-    },
-    (Rule[]){ /* Look it up in ctags database */
-        { ISFILE, "tags", NULL },
-        { EXEC, "grep -q \"^$data\\s\\+\" tags", NULL },
-        { LAUNCH, "picktag fetch tags \"$data\"", NULL },
-        { COMPLETE, NULL, NULL }
-    },
-    (Rule[]){ /* Look up .c or .h files in Code/ */
-        { MATCHES, "data", "\\.[ch]$" },
-        { ISDIR, "Code", NULL },
-        { EXEC, "[[ \"$(find Code -type f -name \"*$data\")\" ]]", NULL },
-        { LAUNCH, "find Code -type f -name \"*$data\" | xargs -r edit", NULL },
-        { COMPLETE, NULL, NULL }
-    },
-    (Rule[]){ /* If it's an existing directory, open it with system default */
-        { ISDIR, "$data", NULL },
-        { LAUNCH, "cd $data && ls -ap | tide -", NULL },
-        { COMPLETE, NULL, NULL }
-    },
-};
+//static Rule* BuiltinRules[] = {
+//    (Rule[]){ /* Match URLS and open them with the browser */
+//        { ISSET, "BROWSER", NULL },
+//        { MATCHES, "data", "^(https?|ftp)://.*" },
+//        { LAUNCH, "$BROWSER \"$M0\"", NULL },
+//        { COMPLETE, NULL, NULL }
+//    },
+//    (Rule[]){ /* Open files with addresses in the editor */
+//        { MATCHES, "data", "^([^:]+):([0-9]+)" },
+//        { ISFILE, "$M1", NULL },
+//        { LAUNCH, "edit \"$M0\"", NULL },
+//        { COMPLETE, NULL, NULL }
+//    },
+//    (Rule[]){ /* Open addresses in the current file */
+//        { MATCHES, "data", "^:?([0-9]+)" },
+//        { ISFILE, "$file", NULL },
+//        { LAUNCH, "edit \"$file:$M1\"", NULL },
+//        { COMPLETE, NULL, NULL }
+//    },
+//    (Rule[]){ /* If it's an existing text file, open it with editor */
+//        { ISFILE, "$data", NULL },
+//        { EXEC, "file --mime \"$file\" | grep -q 'text/'", NULL },
+//        { LAUNCH, "edit \"$file\"", NULL },
+//        { COMPLETE, NULL, NULL }
+//    },
+//    (Rule[]){ /* Look it up in ctags database */
+//        { ISFILE, "tags", NULL },
+//        { EXEC, "grep -q \"^$data\\s\\+\" tags", NULL },
+//        { LAUNCH, "picktag fetch tags \"$data\"", NULL },
+//        { COMPLETE, NULL, NULL }
+//    },
+//    (Rule[]){ /* Look up .c or .h files in Code/ */
+//        { MATCHES, "data", "\\.[ch]$" },
+//        { ISDIR, "Code", NULL },
+//        { EXEC, "[[ \"$(find Code -type f -name \"*$data\")\" ]]", NULL },
+//        { LAUNCH, "find Code -type f -name \"*$data\" | xargs -r edit", NULL },
+//        { COMPLETE, NULL, NULL }
+//    },
+//    (Rule[]){ /* If it's an existing directory, open it with system default */
+//        { ISDIR, "$data", NULL },
+//        { LAUNCH, "cd $data && ls -ap | tide -", NULL },
+//        { COMPLETE, NULL, NULL }
+//    },
+//};
 
 #endif
 
index 6bb2c7af8c2930dc6ce3b2afb400d919fd5ca3f4..c8f4050aa5d587b68f55e5fd9700a1b3c4217212 100644 (file)
@@ -7,17 +7,66 @@
 typedef struct {
     enum {
         COMPLETE=0, MATCHES, IS, ISSET, ISDIR, ISFILE,
-        SET, UNSET, FINDFILE, EXEC, LAUNCH
+        SET, UNSET, EXEC, LAUNCH
     } type;
     char* arg1;
     char* arg2;
 } Rule;
 
 char* Matches[10];
+Rule*** Rulesets = NULL;
+size_t NumRulesets = 0;
 
 #define FETCH_RULES
 #include "config.h"
 
+static Rule*** BuiltinRules = (Rule**[]){
+    (Rule*[]){ /* Match URLS and open them with the browser */
+        &(Rule){ ISSET, "BROWSER", NULL },
+        &(Rule){ MATCHES, "data", "^(https?|ftp)://.*" },
+        &(Rule){ LAUNCH, "$BROWSER \"$M0\"", NULL },
+        &(Rule){ COMPLETE, NULL, NULL }
+    },
+    (Rule*[]){ /* Open files with addresses in the editor */
+        &(Rule){ MATCHES, "data", "^([^:]+):([0-9]+)" },
+        &(Rule){ ISFILE, "$M1", NULL },
+        &(Rule){ LAUNCH, "edit \"$M0\"", NULL },
+        &(Rule){ COMPLETE, NULL, NULL }
+    },
+    (Rule*[]){ /* Open addresses in the current file */
+        &(Rule){ MATCHES, "data", "^:?([0-9]+)" },
+        &(Rule){ ISFILE, "$file", NULL },
+        &(Rule){ LAUNCH, "edit \"$file:$M1\"", NULL },
+        &(Rule){ COMPLETE, NULL, NULL }
+    },
+    (Rule*[]){ /* If it's an existing text file, open it with editor */
+        &(Rule){ ISFILE, "$data", NULL },
+        &(Rule){ EXEC, "file --mime \"$file\" | grep -q 'text/'", NULL },
+        &(Rule){ LAUNCH, "edit \"$file\"", NULL },
+        &(Rule){ COMPLETE, NULL, NULL }
+    },
+    (Rule*[]){ /* Look it up in ctags database */
+        &(Rule){ ISFILE, "tags", NULL },
+        &(Rule){ EXEC, "grep -q \"^$data\\s\\+\" tags", NULL },
+        &(Rule){ LAUNCH, "picktag fetch tags \"$data\"", NULL },
+        &(Rule){ COMPLETE, NULL, NULL }
+    },
+//    (Rule*[]){ /* Look up .c or .h files in Code/ */
+//        &(Rule){ MATCHES, "data", "\\.[ch]$" },
+//        &(Rule){ ISDIR, "Code", NULL },
+//        &(Rule){ EXEC, "[[ \"$(find Code -type f -name \"*$data\")\" ]]", NULL },
+//        &(Rule){ LAUNCH, "find Code -type f -name \"*$data\" | xargs -r edit", NULL },
+//        &(Rule){ COMPLETE, NULL, NULL }
+//    },
+    (Rule*[]){ /* If it's an existing directory, open it with system default */
+        &(Rule){ ISDIR, "$data", NULL },
+        &(Rule){ LAUNCH, "cd $data && ls -ap | tide -", NULL },
+        &(Rule){ COMPLETE, NULL, NULL }
+    },
+    NULL
+};
+
+
 /******************************************************************************/
 
 char* getvar(char* val)
@@ -130,12 +179,6 @@ bool var_unset(char* var)
     return (unsetenv(var) == 0);
 }
 
-bool find_file(char* file)
-{
-    (void)file;
-    return false;
-}
-
 void runcmd(char* cmd)
 {
     char* shellcmd[] = { getvar("SHELL"), "-c", cmd, NULL };
@@ -190,7 +233,6 @@ bool apply_rule(Rule* rule)
         case ISFILE:   ret = var_isfile(rule->arg1);          break;
         case SET:      ret = var_set(rule->arg1, rule->arg2); break;
         case UNSET:    ret = var_unset(rule->arg1);           break;
-        case FINDFILE: ret = find_file(rule->arg1);           break;
         case EXEC:     ret = exec(rule->arg1);                break;
         case LAUNCH:   ret = launch(rule->arg1);              break;
     }
@@ -218,37 +260,215 @@ char* type2str(int type)
         case ISFILE:   ret = "ISFILE";   break;
         case SET:      ret = "SET";      break;
         case UNSET:    ret = "UNSET";    break;
-        case FINDFILE: ret = "FINDFILE"; break;
         case EXEC:     ret = "EXEC";     break;
         case LAUNCH:   ret = "LAUNCH";   break;
     }
     return ret;
 }
 
-int main(int argc, char** argv)
+char* nextline(char** raw)
 {
-    ARGV0 = *argv;
-    if (!argc) usage(ARGV0);
-    setenv("data", *argv, 1);
+    char* line = NULL;
+    char* eol = strchr(*raw, '\n');
+    if (eol)
+    {
+        *eol = '\0';
+        line = *raw;
+        *raw = eol+1;
+    }
+    if (line)
+    {
+        line = strdup(line);
+    }
+    return line;
+}
+
+char* nextfield(char** raw)
+{
+    char* field = *raw;
+    /* skip whitespace */
+    for (; *field && isspace(*field); field++);
+
+    char* eof = field;
+    for (; *eof && !isspace(*eof); eof++);
+    *eof = '\0';
+    *raw = eof + 1;
+
+    return (field && *field ? field : NULL);
+}
+
+void parse_args1(int type, Rule* rule, char* args)
+{
+    rule->type = type;
+    rule->arg1 = args;
+    if (!rule->arg1)
+    {
+        printf("action '%s' requires an argument\n", type2str(type));
+        exit(1);
+    }
+}
+
+void parse_args2(int type, Rule* rule, char* args)
+{
+    rule->type = type;
+    rule->arg1 = nextfield(&args);
+    rule->arg2 = nextfield(&args);
+    if (!rule->arg1 || !rule->arg2)
+    {
+        printf("action '%s' requires two arguments\n", type2str(type));
+        exit(1);
+    }
+}
+
+Rule* parse_rule(char* act, char* args)
+{
+    Rule* rule = calloc(1, sizeof(Rule));
+    if (!strcmp(act, "is"))
+    {
+        parse_args2(IS, rule, args);
+    }
+    else if (!strcmp(act, "isset"))
+    {
+        parse_args1(ISSET, rule, args);
+    }
+    else if (!strcmp(act, "isdir"))
+    {
+        parse_args1(ISDIR, rule, args);
+    }
+    else if (!strcmp(act, "isfile"))
+    {
+        parse_args1(ISFILE, rule, args);
+    }
+    else if (!strcmp(act, "set"))
+    {
+        parse_args2(SET, rule, args);
+    }
+    else if (!strcmp(act, "unset"))
+    {
+        parse_args1(UNSET, rule, args);
+    }
+    else if (!strcmp(act, "exec"))
+    {
+        parse_args1(EXEC, rule, args);
+    }
+    else if (!strcmp(act, "launch"))
+    {
+        parse_args1(LAUNCH, rule, args);
+    }
+    else if (!strcmp(act, "matches"))
+    {
+        parse_args2(MATCHES, rule, args);
+    }
+    else
+    {
+        printf("error: unknown action '%s'\n", act);
+        exit(1);
+    }
+    return rule;
+}
+
+void add_ruleset(Rule** rules, size_t nrules, Rule* rule)
+{
+    if (rules)
+    {
+        rules = realloc(rules, sizeof(Rule) * ++nrules);
+        rules[nrules-1] = rule;
+    }
+    Rulesets = realloc(Rulesets, sizeof(Rule) * ++NumRulesets);
+    Rulesets[NumRulesets-1] = rules;
+}
 
-    telem_send("FETCH(%s)\n", *argv);
-    for (unsigned int i = 0; i < nelem(BuiltinRules); i++)
+void parse_rules(char* path)
+{
+    char* line = NULL;
+    char* raw = readfile(path);
+    telem_send("RULES: %s\n", path);
+    if (raw)
     {
-        Rule* rule = BuiltinRules[i];
-        telem_send("RULESET(%d)\n", i);
+        Rule** rules = NULL;
+        size_t nrules = 0;
 
-        for (; rule->type != COMPLETE; rule++)
+        while ((line = nextline(&raw)) != NULL)
         {
-            telem_send("  RULE(%s '%s' '%s')\n", type2str(rule->type), rule->arg1, rule->arg2);
-            if (!apply_rule(rule))
+            char* cmd = line;
+            /* skip whitespace */
+            for (; *cmd && isspace(*cmd); cmd++);
+
+            if (!*cmd)
+            {
+                add_ruleset(rules, nrules, calloc(1, sizeof(Rule)));
+                rules = NULL;
+                nrules = 0;
+            }
+            else if (*cmd != '#')
+            {
+                char* action = nextfield(&cmd);
+                rules = realloc(rules, sizeof(Rule) * ++nrules);
+                rules[nrules-1] = parse_rule(action, cmd);
+            }
+            else
             {
-                break;
+                free(line);
             }
+            line = NULL;
         }
-        if (rule->type == COMPLETE)
+
+        if (rules)
+        {
+            add_ruleset(rules, nrules, calloc(1, sizeof(Rule)));
+        }
+        add_ruleset(NULL, 0, NULL);
+    }
+    telem_send("DONE\n", path);
+}
+
+void check_ruleset(Rule** rule)
+{
+    for (; (*rule)->type != COMPLETE; rule++)
+    {
+        telem_send("  RULE(%s '%s' '%s')\n", type2str((*rule)->type), (*rule)->arg1, (*rule)->arg2);
+        if (!apply_rule(*rule))
+        {
+            break;
+        }
+    }
+
+    if ((*rule)->type == COMPLETE)
+    {
+        telem_send("  ACCEPT\n");
+        exit(0);
+    }
+    else
+    {
+        telem_send("  NEXT\n");
+    }
+}
+
+int main(int argc, char** argv)
+{
+    ARGV0 = *argv;
+    if (argc != 2)
+    {
+        usage(ARGV0);
+    }
+    else
+    {
+        /* load rules from files */
+        parse_rules("fetch.rules");
+
+        telem_send("FETCH(%s)\n", argv[1]);
+        setenv("data", argv[1], 1);
+
+        /* process parsed rules */
+        for (; Rulesets && *Rulesets; Rulesets++)
+        {
+            check_ruleset(*Rulesets);
+        }
+
+        /* process builtin rules */
+        for (unsigned int i = 0; BuiltinRules[i]; i++)
         {
-            telem_send("  ACCEPT\n");
-            exit(0);
+            check_ruleset(BuiltinRules[i]);
         }
     }
     return 1;
index d7448d9e619ad011db00591cb8f62e54b838d17b..938e75da9ba88bf3a1f9327e1d0724f0fd5a7f1a 100644 (file)
@@ -192,7 +192,7 @@ static void xmousebtn(XConf* x, XEvent* e)
         }
         case MouseActFetch:
         {
-            FetchCmd[2] = view_fetch(win_view(Focused), row, col, risfile);
+            FetchCmd[1] = view_fetch(win_view(Focused), row, col, risfile);
             if (job_run(FetchCmd) != 0)
             {
                 SearchDir *= (win_keymodsset(ModShift) ? -1 : +1);
@@ -201,7 +201,7 @@ static void xmousebtn(XConf* x, XEvent* e)
                 view_findstr(win_view(EDIT), SearchDir, SearchTerm);
                 SyncMouse = true;
             }
-            free(FetchCmd[2]);
+            free(FetchCmd[1]);
             break;
         }
         case MouseActScrollUp: