--- /dev/null
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2014 Daniel Carl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#include "config.h"
+#ifdef FEATURE_AUTOCMD
+#include "autocmd.h"
+#include "ascii.h"
+#include "ex.h"
+
+typedef struct {
+ guint bits;
+ char *excmd;
+ char *pattern;
+} AutoCmd;
+
+typedef struct {
+ char *name;
+ GSList *cmds;
+} AuGroup;
+
+static struct {
+ const char *name;
+ guint bits;
+} events[] = {
+ {"*", 0x001f},
+ {"PageLoadProvisional", 0x0001},
+ {"PageLoadCommited", 0x0002},
+ {"PageLoadFirstLayout", 0x0004},
+ {"PageLoadFinished", 0x0008},
+ {"PageLoadFailed", 0x0010},
+};
+
+static AuGroup *curgroup = NULL;
+static GSList *groups = NULL;
+
+static guint get_event_bits(const char *name);
+static AuGroup *get_group(const char *name);
+static char *get_next_word(char **line);
+static AuGroup *new_group(const char *name);
+static void free_group(AuGroup *group);
+static AutoCmd *new_autocmd(const char *excmd, const char *pattern);
+static void free_autocmd(AutoCmd *cmd);
+
+
+void autocmd_init(void)
+{
+ curgroup = new_group("end");
+ groups = g_slist_prepend(groups, curgroup);
+}
+
+void autocmd_cleanup(void)
+{
+ if (groups) {
+ g_slist_free_full(groups, (GDestroyNotify)free_group);
+ }
+}
+
+/**
+ * Handle the :augroup {group} ex command.
+ */
+gboolean autocmd_augroup(char *name, gboolean delete)
+{
+ AuGroup *grp;
+
+ if (!*name) {
+ return false;
+ }
+
+ /* check for group "end" that marks the default group */
+ if (!strcmp(name, "end")) {
+ curgroup = (AuGroup*)groups->data;
+ return true;
+ }
+
+ /* check if the group does already exists */
+ grp = get_group(name);
+ if (grp) {
+ /* if the group is found in the known groups use it as current */
+ curgroup = grp;
+
+ return true;
+ }
+
+ /* create a new group and use it as current */
+ curgroup = new_group(name);
+
+ /* append it to known groups */
+ groups = g_slist_prepend(groups, curgroup);
+
+ return true;
+}
+
+/**
+ * Add or delete auto commands.
+ *
+ * :au[tocmd]! [group] {event} {pat} [nested] {cmd}
+ * group and nested flag are not supported at the moment.
+ */
+gboolean autocmd_add(char *name, gboolean delete)
+{
+ guint bits;
+ char *parse, *word, *pattern, *excmd;
+ AuGroup *grp;
+
+ parse = name;
+
+ /* parse group name if it's there */
+ word = get_next_word(&parse);
+ if (word) {
+ /* check if the word is a known group name */
+ grp = get_group(word);
+ if (grp) {
+ /* group is found - get the next word */
+ word = get_next_word(&parse);
+ } else {
+ /* no group found - use the current one */
+ grp = curgroup;
+ }
+ }
+
+ /* parse event name - if none was matched run it for all events */
+ if (word) {
+ bits = get_event_bits(word);
+ if (!bits) {
+ return false;
+ }
+ word = get_next_word(&parse);
+ } else {
+ bits = events[AU_ALL].bits;
+ }
+
+ /* last word is the pattern - if not found use '*' */
+ pattern = word ? word : "*";
+
+ /* the rest of the line becoes the ex command to run */
+ if (parse && !*parse) {
+ parse = NULL;
+ }
+ excmd = parse;
+
+ /* delete the autocmd if bang was given */
+ if (delete) {
+ GSList *lc;
+ AutoCmd *cmd;
+
+ /* check if the group does already exists */
+ for (lc = grp->cmds; lc; lc = lc->next) {
+ cmd = (AutoCmd*)lc->data;
+ /* if not bits match - skip the command */
+ if (!(cmd->bits & bits)) {
+ continue;
+ }
+ /* skip it pattern does not match - '*' matches everithing */
+ if (strcmp(cmd->pattern, "*") && strcmp(cmd->pattern, pattern)) {
+ continue;
+ }
+ /* remove the bits from the item */
+ cmd->bits &= ~bits;
+
+ /* if the command has no matching events - remove it */
+ grp->cmds = g_slist_delete_link(grp->cmds, lc);
+ free_autocmd(cmd);
+ }
+
+ return true;
+ }
+
+ /* add the new autocmd */
+ if (excmd) {
+ AutoCmd *cmd = new_autocmd(excmd, pattern);
+ cmd->bits = bits;
+
+ /* add the new autocmd to the group */
+ grp->cmds = g_slist_append(grp->cmds, cmd);
+ }
+
+ return true;
+}
+
+/**
+ * Run named auto command.
+ */
+gboolean autocmd_run(const char *group, AuEvent event, const char *uri)
+{
+ GSList *lg, *lc;
+ AuGroup *grp;
+ AutoCmd *cmd;
+ guint bits = events[event].bits;
+
+ /* loop over the groups and find matching commands */
+ for (lg = groups; lg; lg = lg->next) {
+ grp = lg->data;
+ /* if a group was given - skip all none matching groupes */
+ if (group && strcmp(group, grp->name)) {
+ continue;
+ }
+ /* test each command in group */
+ for (lc = grp->cmds; lc; lc = lc->next) {
+ cmd = lc->data;
+ /* skip if this dos not match the event bits */
+ if (!(bits & cmd->bits)) {
+ continue;
+ }
+ /* TODO skip if pattern does not match */
+ /* run the command */
+ /* TODO shoult hte result be tested for RESULT_COMPLETE? */
+ ex_run_string(cmd->excmd);
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Get the augroup by it's name.
+ */
+static AuGroup *get_group(const char *name)
+{
+ GSList *lg;
+ AuGroup *grp;
+
+ for (lg = groups; lg; lg = lg->next) {
+ grp = lg->data;
+ if (!strcmp(grp->name, name)) {
+ return grp;
+ }
+ }
+
+ return NULL;
+}
+
+static guint get_event_bits(const char *name)
+{
+ int result = 0;
+
+ while (*name) {
+ int i, len;
+ for (i = 0; i < LENGTH(events); i++) {
+ /* we count the chars that given name has in common with the
+ * current processed event */
+ len = 0;
+ while (name[len] && events[i].name[len] && name[len] == events[i].name[len]) {
+ len++;
+ }
+
+ /* check if the matching chars built a full word match */
+ if (events[i].name[len] == '\0'
+ && (name[len] == '\0' || name[len] == ',')
+ ) {
+ /* add the bits to the result */
+ result |= events[i].bits;
+
+ /* move pointer after the match and skip possible ',' */
+ name += len;
+ if (*name == ',') {
+ name++;
+ }
+ break;
+ }
+ }
+
+ /* check if the end was reached without a match */
+ if (i >= LENGTH(events)) {
+ /* is this the right place to write the error */
+ vb_echo(VB_MSG_ERROR, true, "Bad autocmd event name: %s", name);
+ return 0;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Get the next word from given line.
+ * Given line pointer is set past the word and and a 0-byte is added there.
+ */
+static char *get_next_word(char **line)
+{
+ char *word;
+
+ if (!*line || !**line) {
+ return NULL;
+ }
+
+ /* remember where the word starts */
+ word = *line;
+
+ /* move pointer to the end of the word or of the line */
+ while (**line && !VB_IS_SPACE(**line)) {
+ (*line)++;
+ }
+
+ /* end the word */
+ if (**line) {
+ *(*line)++ = '\0';
+ }
+
+ /* skip trailing whitespace */
+ while (VB_IS_SPACE(**line)) {
+ (*line)++;
+ }
+
+ return word;
+}
+
+static AuGroup *new_group(const char *name)
+{
+ AuGroup *new = g_slice_new(AuGroup);
+ new->name = g_strdup(name);
+ new->cmds = NULL;
+
+ return new;
+}
+
+static void free_group(AuGroup *group)
+{
+ g_free(group->name);
+ if (group->cmds) {
+ g_slist_free_full(group->cmds, (GDestroyNotify)free_autocmd);
+ }
+ g_slice_free(AuGroup, group);
+}
+
+static AutoCmd *new_autocmd(const char *excmd, const char *pattern)
+{
+ AutoCmd *new = g_slice_new(AutoCmd);
+ new->excmd = g_strdup(excmd);
+ new->pattern = g_strdup(pattern);
+ return new;
+}
+
+static void free_autocmd(AutoCmd *cmd)
+{
+ g_free(cmd->excmd);
+ g_free(cmd->pattern);
+ g_slice_free(AutoCmd, cmd);
+}
+
+#endif
#include "map.h"
#include "js.h"
#include "normal.h"
+#ifdef FEATURE_AUTOCMD
+#include "autocmd.h"
+#endif
typedef enum {
+#ifdef FEATURE_AUTOCMD
+ EX_AUTOCMD,
+ EX_AUGROUP,
+#endif
EX_BMA,
EX_BMR,
EX_EVAL,
static void free_cmdarg(ExArg *arg);
static gboolean execute(const ExArg *arg);
+static gboolean ex_augroup(const ExArg *arg);
+static gboolean ex_autocmd(const ExArg *arg);
static gboolean ex_bookmark(const ExArg *arg);
static gboolean ex_eval(const ExArg *arg);
static gboolean ex_hardcopy(const ExArg *arg);
* match. */
static ExInfo commands[] = {
/* command code func flags */
+#ifdef FEATURE_AUTOCMD
+ {"autocmd", EX_AUTOCMD, ex_autocmd, EX_FLAG_CMD|EX_FLAG_BANG},
+ {"augroup", EX_AUGROUP, ex_augroup, EX_FLAG_LHS|EX_FLAG_BANG},
+#endif
{"bma", EX_BMA, ex_bookmark, EX_FLAG_RHS},
{"bmr", EX_BMR, ex_bookmark, EX_FLAG_RHS},
{"cmap", EX_CMAP, ex_map, EX_FLAG_LHS|EX_FLAG_RHS},
g_slice_free(ExArg, arg);
}
+#ifdef FEATURE_AUTOCMD
+static gboolean ex_augroup(const ExArg *arg)
+{
+ return autocmd_augroup(arg->lhs->str, arg->bang);
+}
+
+static gboolean ex_autocmd(const ExArg *arg)
+{
+ return autocmd_add(arg->rhs->str, arg->bang);
+}
+#endif
+
static gboolean ex_bookmark(const ExArg *arg)
{
if (arg->code == EX_BMR) {