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 ";
#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
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)
return (unsetenv(var) == 0);
}
-bool find_file(char* file)
-{
- (void)file;
- return false;
-}
-
void runcmd(char* cmd)
{
char* shellcmd[] = { getvar("SHELL"), "-c", cmd, NULL };
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;
}
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;