diff options
author | uranther <jwheaton@purdue.edu> | 2009-06-01 13:44:45 -0400 |
---|---|---|
committer | uranther <jwheaton@purdue.edu> | 2009-06-01 13:44:45 -0400 |
commit | 63a00498cb90e8a05ad950601dd55eb3ca361dbb (patch) | |
tree | dc54ec929372b7bb225b5730d3469609440b97fe /uzbl.c | |
parent | 0683d83a8058b36a4f351022afa8faddbae506ad (diff) | |
parent | 22f1e1bd7d2393b526f88929b6a980b142d92856 (diff) |
merge with Dieterbe/experimental
Diffstat (limited to 'uzbl.c')
-rw-r--r-- | uzbl.c | 465 |
1 files changed, 312 insertions, 153 deletions
@@ -81,19 +81,20 @@ GOptionEntry entries[] = typedef const struct { void **ptr; int type; + int dump; void (*func)(void); } uzbl_cmdprop; enum {TYPE_INT, TYPE_STR, TYPE_FLOAT}; /* an abbreviation to help keep the table's width humane */ -#define PTR(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .func = fun } +#define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun } const struct { char *name; uzbl_cmdprop cp; } var_name_to_ptr[] = { -/* variable name pointer to variable in code type callback function */ +/* variable name pointer to variable in code type dump callback function */ /* --------------------------------------------------------------------------------------- */ { "uri", PTR(uzbl.state.uri, STR, cmd_load_uri)}, { "mode", PTR(uzbl.behave.mode, INT, NULL)}, @@ -184,8 +185,51 @@ make_var_to_name_hash() { } } - /* --- UTILITY FUNCTIONS --- */ +static gchar * +expand_vars(char *s) { + uzbl_cmdprop *c; + char upto = ' '; + char ret[256], *vend; + GString *buf = g_string_new(""); + + while(*s) { + switch(*s) { + case '\\': + g_string_append_c(buf, *++s); + s++; + break; + case '@': + if(*(s+1) == '{') { + upto = '}'; s++; + } + s++; + if( (vend = strchr(s, upto)) || + (vend = strchr(s, '\0')) ) { + strncpy(ret, s, vend-s); + ret[vend-s] = '\0'; + if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) { + if(c->type == TYPE_STR) + g_string_append(buf, (gchar *)*c->ptr); + else if(c->type == TYPE_INT) { + char *b = itos((int)*c->ptr); + g_string_append(buf, b); + g_free(b); + } + } + if(upto == ' ') s = vend; + else s = vend+1; + upto = ' '; + } + break; + default: + g_string_append_c(buf, *s); + s++; + break; + } + } + return g_string_free(buf, FALSE); +} char * itos(int val) { @@ -387,23 +431,27 @@ scroll (GtkAdjustment* bar, GArray *argv) { gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount); } -static void scroll_begin(WebKitWebView* page, GArray *argv) { +static void +scroll_begin(WebKitWebView* page, GArray *argv) { (void) page; (void) argv; gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v)); } -static void scroll_end(WebKitWebView* page, GArray *argv) { +static void +scroll_end(WebKitWebView* page, GArray *argv) { (void) page; (void) argv; gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) - gtk_adjustment_get_page_size(uzbl.gui.bar_v)); } -static void scroll_vert(WebKitWebView* page, GArray *argv) { +static void +scroll_vert(WebKitWebView* page, GArray *argv) { (void) page; scroll(uzbl.gui.bar_v, argv); } -static void scroll_horz(WebKitWebView* page, GArray *argv) { +static void +scroll_horz(WebKitWebView* page, GArray *argv) { (void) page; scroll(uzbl.gui.bar_h, argv); } @@ -558,8 +606,15 @@ static struct {char *name; Command command[2];} cmdlist[] = { "search_reverse", {search_reverse_text, NOSPLIT} }, { "dehilight", {dehilight, 0} }, { "toggle_insert_mode", {toggle_insert_mode, 0} }, - { "runcmd", {runcmd, NOSPLIT} }, - { "set", {set_var, NOSPLIT} } + { "set", {set_var, NOSPLIT} }, + //{ "get", {get_var, NOSPLIT} }, + { "bind", {act_bind, NOSPLIT} }, + { "dump_config", {act_dump_config, 0} }, + { "keycmd", {keycmd, NOSPLIT} }, + { "keycmd_nl", {keycmd_nl, NOSPLIT} }, + { "keycmd_bs", {keycmd_bs, 0} }, + { "chain", {chain, 0} }, + { "print", {print, NOSPLIT} } }; static void @@ -604,11 +659,37 @@ file_exists (const char * filename) { static void set_var(WebKitWebView *page, GArray *argv) { (void) page; - gchar *ctl_line; + gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2); + gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " ")); + set_var_value(g_strstrip(split[0]), value); + g_free(value); + g_strfreev(split); +} - ctl_line = g_strdup_printf("%s %s", "set", argv_idx(argv, 0)); - parse_cmd_line(ctl_line); - g_free(ctl_line); +static void +print(WebKitWebView *page, GArray *argv) { + (void) page; + gchar* buf; + + buf = expand_vars(argv_idx(argv, 0)); + puts(buf); + g_free(buf); +} + +static void +act_bind(WebKitWebView *page, GArray *argv) { + (void) page; + gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2); + gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " ")); + add_binding(g_strstrip(split[0]), value); + g_free(value); + g_strfreev(split); +} + + +static void +act_dump_config() { + dump_config(); } static void @@ -734,6 +815,45 @@ new_window_load_uri (const gchar * uri) { } static void +chain (WebKitWebView *page, GArray *argv) { + (void)page; + gchar *a = NULL; + gchar **parts = NULL; + guint i = 0; + while ((a = argv_idx(argv, i++))) { + parts = g_strsplit (a, " ", 2); + parse_command(parts[0], parts[1]); + g_strfreev (parts); + } +} + +static void +keycmd (WebKitWebView *page, GArray *argv) { + (void)page; + (void)argv; + g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0)); + run_keycmd(FALSE); + update_title(); +} + +static void +keycmd_nl (WebKitWebView *page, GArray *argv) { + (void)page; + (void)argv; + g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0)); + run_keycmd(TRUE); + update_title(); +} + +static void +keycmd_bs (WebKitWebView *page, GArray *argv) { + (void)page; + (void)argv; + g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1); + update_title(); +} + +static void close_uzbl (WebKitWebView *page, GArray *argv) { (void)page; (void)argv; @@ -893,7 +1013,8 @@ expand_template(const char *template, gboolean escape_markup) { break; case SYM_MODE: g_string_append(ret, - uzbl.behave.insert_mode?"[I]":"[C]"); + uzbl.behave.insert_mode? + uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator); break; case SYM_MSG: g_string_append(ret, @@ -1012,6 +1133,9 @@ run_command (const gchar *command, const guint npre, const gchar **args, g_string_append_printf(s, " -- result: %s", (result ? "true" : "false")); printf("%s\n", s->str); g_string_free(s, TRUE); + if(stdout) { + printf("Stdout: %s\n", *stdout); + } } if (err) { g_printerr("error on run_command: %s\n", err->message); @@ -1144,34 +1268,6 @@ parse_command(const char *cmd, const char *param) { g_printerr ("command \"%s\" not understood. ignoring.\n", cmd); } -/* command parser */ -static void -setup_regex() { - uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$", - G_REGEX_OPTIMIZE, 0, NULL); - uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$", - G_REGEX_OPTIMIZE, 0, NULL); - uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$", - G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL); - uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$", - G_REGEX_OPTIMIZE, 0, NULL); - uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$", - G_REGEX_OPTIMIZE, 0, NULL); -} - -static gboolean -get_var_value(gchar *name) { - uzbl_cmdprop *c; - - if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) { - if(c->type == TYPE_STR) - printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr); - else if(c->type == TYPE_INT) - printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr); - } - return TRUE; -} - static void set_proxy_url() { SoupURI *suri; @@ -1265,7 +1361,7 @@ cmd_disable_plugins() { static void cmd_disable_scripts() { g_object_set (G_OBJECT(view_settings()), "enable-scripts", - !uzbl.behave.disable_plugins, NULL); + !uzbl.behave.disable_scripts, NULL); } static void @@ -1338,6 +1434,7 @@ cmd_caret_browsing() { static void cmd_cookie_handler() { gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2); + /* pitfall: doesn't handle chain actions; must the sync_ action manually */ if ((g_strcmp0(split[0], "sh") == 0) || (g_strcmp0(split[0], "spawn") == 0)) { g_free (uzbl.behave.cookie_handler); @@ -1422,10 +1519,12 @@ static gboolean set_var_value(gchar *name, gchar *val) { uzbl_cmdprop *c = NULL; char *endp = NULL; + char *buf = NULL; if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) { /* check for the variable type */ if (c->type == TYPE_STR) { + buf = expand_vars(val); g_free(*c->ptr); *c->ptr = g_strdup(val); } else if (c->type == TYPE_INT) { @@ -1443,12 +1542,6 @@ set_var_value(gchar *name, gchar *val) { } static void -runcmd(WebKitWebView* page, GArray *argv) { - (void) page; - parse_cmd_line(argv_idx(argv, 0)); -} - -static void render_html() { Behaviour *b = &uzbl.behave; @@ -1463,90 +1556,42 @@ render_html() { enum {M_CMD, M_HTML}; static void parse_cmd_line(const char *ctl_line) { - gchar **tokens = NULL; Behaviour *b = &uzbl.behave; + size_t len=0; if(b->mode == M_HTML) { - - if(!strncmp(b->html_endmarker, ctl_line, strlen(b->html_endmarker))) { + len = strlen(b->html_endmarker); + /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */ + if(len == strlen(ctl_line)-1 && + !strncmp(b->html_endmarker, ctl_line, len)) { set_timeout(0); set_var_value("mode", "0"); render_html(); return; } else { - /* set an alarm to kill us after the timeout */ set_timeout(b->html_timeout); g_string_append(b->html_buffer, ctl_line); } } - else { - /* SET command */ - if(ctl_line[0] == 's' || ctl_line[0] == 'S') { - tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0); - if(tokens[0][0] == 0) { - gchar* value = parseenv(g_strdup(tokens[2])); - set_var_value(tokens[1], value); - g_free(value); - } - else - printf("Error in command: %s\n", tokens[0]); - } - /* GET command */ - else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') { - tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0); - if(tokens[0][0] == 0) { - get_var_value(tokens[1]); - } - else - printf("Error in command: %s\n", tokens[0]); - } - /* BIND command */ - else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') { - tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0); - if(tokens[0][0] == 0) { - gchar* value = parseenv(g_strdup(tokens[2])); - add_binding(tokens[1], value); - g_free(value); - } - else - printf("Error in command: %s\n", tokens[0]); - } - /* ACT command */ - else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') { - tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0); - if(tokens[0][0] == 0) { - parse_command(tokens[1], tokens[2]); - } - else - printf("Error in command: %s\n", tokens[0]); - } - /* KEYCMD command */ - else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') { - tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0); - if(tokens[0][0] == 0) { - /* should incremental commands want each individual "keystroke" - sent in a loop or the whole string in one go like now? */ - g_string_assign(uzbl.state.keycmd, tokens[1]); - run_keycmd(FALSE); - if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N")) - run_keycmd(TRUE); - update_title(); - } - } - /* Comments */ - else if( (ctl_line[0] == '#') - || (ctl_line[0] == ' ') - || (ctl_line[0] == '\n')) - ; /* ignore these lines */ - else - printf("Command not understood (%s)\n", ctl_line); - - if(tokens) - g_strfreev(tokens); + else if((ctl_line[0] == '#') /* Comments */ + || (ctl_line[0] == ' ') + || (ctl_line[0] == '\n')) + ; /* ignore these lines */ + else { /* parse a command */ + gchar *ctlstrip; + gchar **tokens = NULL; + len = strlen(ctl_line); + + if (ctl_line[len - 1] == '\n') /* strip trailing newline */ + ctlstrip = g_strndup(ctl_line, len - 1); + else ctlstrip = g_strdup(ctl_line); + + tokens = g_strsplit(ctlstrip, " ", 2); + parse_command(tokens[0], tokens[1]); + g_free(ctlstrip); + g_strfreev(tokens); } - - return; } static gchar* @@ -1848,10 +1893,8 @@ key_press_cb (GtkWidget* window, GdkEventKey* event) return TRUE; } - if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) { - g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1); - update_title(); - } + if (event->keyval == GDK_BackSpace) + keycmd_bs(NULL, NULL); gboolean key_ret = FALSE; if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter)) @@ -1973,29 +2016,105 @@ GtkWidget* create_window () { return window; } +static gchar** +inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) { + /* + If actname is one that calls an external command, this function will inject + newargs in front of the user-provided args in that command line. They will + come become after the body of the script (in sh) or after the name of + the command to execute (in spawn). + i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and + span <command> <userargs> becomes spawn <command> <ARGS> <userargs>. + + The return value consist of two strings: the action (sh, ...) and its args. + + If act is not one that calls an external command, then the given action merely + gets duplicated. + */ + GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*)); + gchar *actdup = g_strdup(actname); + g_array_append_val(rets, actdup); + + if ((g_strcmp0(actname, "spawn") == 0) || + (g_strcmp0(actname, "sh") == 0) || + (g_strcmp0(actname, "sync_spawn") == 0) || + (g_strcmp0(actname, "sync_sh") == 0)) { + guint i; + GString *a = g_string_new(""); + gchar **spawnparts = split_quoted(origargs, FALSE); + g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */ + if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */ + + for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */ + if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]); + + g_array_append_val(rets, a->str); + g_string_free(a, FALSE); + g_strfreev(spawnparts); + } else { + gchar *origdup = g_strdup(origargs); + g_array_append_val(rets, origdup); + } + return (gchar**)g_array_free(rets, FALSE); +} + static void run_handler (const gchar *act, const gchar *args) { + /* Consider this code a temporary hack to make the handlers usable. + In practice, all this splicing, injection, and reconstruction is + inefficient, annoying and hard to manage. Potential pitfalls arise + when the handler specific args 1) are not quoted (the handler + callbacks should take care of this) 2) are quoted but interfere + with the users' own quotation. A more ideal solution is + to refactor parse_command so that it doesn't just take a string + and execute it; rather than that, we should have a function which + returns the argument vector parsed from the string. This vector + could be modified (e.g. insert additional args into it) before + passing it to the next function that actually executes it. Though + it still isn't perfect for chain actions.. will reconsider & re- + factor when I have the time. -duc */ + char **parts = g_strsplit(act, " ", 2); if (!parts) return; - else if ((g_strcmp0(parts[0], "spawn") == 0) - || (g_strcmp0(parts[0], "sh") == 0) - || (g_strcmp0(parts[0], "sync_spawn") == 0) - || (g_strcmp0(parts[0], "sync_sh") == 0)) { - guint i; - GString *a = g_string_new (""); - char **spawnparts; - spawnparts = split_quoted(parts[1], FALSE); - g_string_append_printf(a, "%s", spawnparts[0]); - if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */ + if (g_strcmp0(parts[0], "chain") == 0) { + GString *newargs = g_string_new(""); + gchar **chainparts = split_quoted(parts[1], FALSE); - for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */ - g_string_append_printf(a, " %s", spawnparts[i]); - parse_command(parts[0], a->str); - g_string_free (a, TRUE); - g_strfreev (spawnparts); - } else - parse_command(parts[0], parts[1]); - g_strfreev (parts); + /* for every argument in the chain, inject the handler args + and make sure the new parts are wrapped in quotes */ + gchar **cp = chainparts; + gchar quot = '\''; + gchar *quotless = NULL; + gchar **spliced_quotless = NULL; // sigh -_-; + gchar **inpart = NULL; + + while (*cp) { + if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */ + quot = **cp; + quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2); + } else quotless = g_strdup(*cp); + + spliced_quotless = g_strsplit(quotless, " ", 2); + inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args); + g_strfreev(spliced_quotless); + + g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot); + g_free(quotless); + g_strfreev(inpart); + cp++; + } + + parse_command(parts[0], &(newargs->str[1])); + g_string_free(newargs, TRUE); + g_strfreev(chainparts); + + } else { + gchar **inparts = inject_handler_args(parts[0], parts[1], args); + parse_command(inparts[0], inparts[1]); + g_free(inparts[0]); + g_free(inparts[1]); + } + g_strfreev(parts); } static void @@ -2100,7 +2219,8 @@ settings_init () { static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){ (void) session; (void) user_data; - if (!uzbl.behave.cookie_handler) return; + if (!uzbl.behave.cookie_handler) + return; soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL); GString *s = g_string_new (""); @@ -2108,10 +2228,13 @@ static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer use g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path); run_handler(uzbl.behave.cookie_handler, s->str); - if(uzbl.comm.sync_stdout) - soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout); - //printf("stdout: %s\n", uzbl.comm.sync_stdout); // debugging - if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout); + if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) { + char *p = strchr(uzbl.comm.sync_stdout, '\n' ); + if ( p != NULL ) *p = '\0'; + soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout); + } + if (uzbl.comm.sync_stdout) + uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout); g_string_free(s, TRUE); } @@ -2225,7 +2348,34 @@ set_up_inspector() { g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL); } +static void +dump_var_hash(gpointer k, gpointer v, gpointer ud) { + (void) ud; + uzbl_cmdprop *c = v; + + if(!c->dump) + return; + + if(c->type == TYPE_STR) + printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" "); + else if(c->type == TYPE_INT) + printf("set %s = %d\n", (char *)k, (int)*c->ptr); +} + +static void +dump_key_hash(gpointer k, gpointer v, gpointer ud) { + (void) ud; + Action *a = v; + printf("bind %s = %s %s\n", (char *)k , + (char *)a->name, a->param?(char *)a->param:""); +} + +static void +dump_config() { + g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL); + g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL); +} /** -- MAIN -- **/ int @@ -2242,6 +2392,10 @@ main (int argc, char* argv[]) { g_option_context_add_group (context, gtk_get_option_group (TRUE)); g_option_context_parse (context, &argc, &argv, NULL); g_option_context_free(context); + + gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL); + gboolean verbose_override = uzbl.state.verbose; + /* initialize hash table */ uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action); @@ -2269,7 +2423,10 @@ main (int argc, char* argv[]) { uzbl.behave.html_timeout = 60; uzbl.behave.base_url = g_strdup("http://invalid"); - setup_regex(); + /* default mode indicators */ + uzbl.behave.insert_indicator = g_strdup("I"); + uzbl.behave.cmd_indicator = g_strdup("C"); + setup_scanner(); commands_hash (); make_var_to_name_hash(); @@ -2316,12 +2473,14 @@ main (int argc, char* argv[]) { create_stdin(); - if(uzbl.state.uri) { - GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*)); - g_array_append_val(a, uzbl.state.uri); - load_uri (uzbl.gui.web_view, a); - g_array_free (a, TRUE); - } + if (verbose_override > uzbl.state.verbose) + uzbl.state.verbose = verbose_override; + + if (uri_override) { + set_var_value("uri", uri_override); + g_free(uri_override); + } else if (uzbl.state.uri) + cmd_load_uri(uzbl.gui.web_view, NULL); gtk_main (); clean_up(); |