1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
60 #include <sys/ioctl.h>
66 /* commandline arguments (set initial values for the state variables) */
68 GOptionEntry entries[] =
70 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
71 "Uri to load at startup (equivalent to 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "URI" },
72 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
73 "Whether to print all messages or just errors.", NULL },
74 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
75 "Name of the current instance (defaults to Xorg window id)", "NAME" },
76 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
77 "Path to config file or '-' for stdin", "FILE" },
78 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
79 "Socket ID", "SOCKET" },
80 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
81 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
82 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
83 "Print the version and exit", NULL },
84 { NULL, 0, 0, 0, NULL, NULL, NULL }
87 /* associate command names to their properties */
89 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
90 the PTR() macro is kind of preventing this change at the moment. */
98 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
100 /* abbreviations to help keep the table's width humane */
101 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
102 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
107 } var_name_to_ptr[] = {
108 /* variable name pointer to variable in code type dump callback function */
109 /* ---------------------------------------------------------------------------------------------- */
110 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
111 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
112 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
113 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
114 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
115 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
116 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
117 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
118 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
119 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
120 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
121 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
122 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
123 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
124 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
125 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
126 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_indicator)},
127 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
128 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
129 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
130 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
131 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
132 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
133 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
134 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
135 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
136 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
137 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
138 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
139 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
140 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
141 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
142 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
143 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
144 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
145 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
146 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
147 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
148 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
149 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
151 /* exported WebKitWebSettings properties */
152 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
153 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
154 { "default_font_family", PTR_V(uzbl.behave.default_font_family, STR, 1, cmd_default_font_family)},
155 { "monospace_font_family", PTR_V(uzbl.behave.monospace_font_family, STR, 1, cmd_monospace_font_family)},
156 { "cursive_font_family", PTR_V(uzbl.behave.cursive_font_family, STR, 1, cmd_cursive_font_family)},
157 { "sans_serif_font_family", PTR_V(uzbl.behave.sans_serif_font_family, STR, 1, cmd_sans_serif_font_family)},
158 { "serif_font_family", PTR_V(uzbl.behave.serif_font_family, STR, 1, cmd_serif_font_family)},
159 { "fantasy_font_family", PTR_V(uzbl.behave.fantasy_font_family, STR, 1, cmd_fantasy_font_family)},
160 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
161 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
162 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
163 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
164 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
165 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
166 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
167 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
168 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
169 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
170 { "resizable_text_areas", PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
171 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
172 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
173 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
175 /* constants (not dumpable or writeable) */
176 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
177 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
178 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
179 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
180 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
181 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
182 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
183 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
184 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
185 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
186 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
188 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
189 }, *n2v_p = var_name_to_ptr;
196 { "SHIFT", GDK_SHIFT_MASK }, // shift
197 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
198 { "CONTROL", GDK_CONTROL_MASK }, // control
199 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
200 { "MOD2", GDK_MOD2_MASK }, // 5th mod
201 { "MOD3", GDK_MOD3_MASK }, // 6th mod
202 { "MOD4", GDK_MOD4_MASK }, // 7th mod
203 { "MOD5", GDK_MOD5_MASK }, // 8th mod
204 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
205 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
206 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
207 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
208 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
209 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
210 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
211 { "META", GDK_META_MASK }, // meta (since 2.10)
216 /* construct a hash from the var_name_to_ptr array for quick access */
218 make_var_to_name_hash() {
219 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
221 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
226 /* --- UTILITY FUNCTIONS --- */
227 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
229 get_exp_type(gchar *s) {
233 else if(*(s+1) == '{')
234 return EXP_BRACED_VAR;
235 else if(*(s+1) == '<')
237 else if(*(s+1) == '[')
240 return EXP_SIMPLE_VAR;
246 * recurse == 1: don't expand '@(command)@'
247 * recurse == 2: don't expand '@<java script>@'
250 expand(char *s, guint recurse) {
253 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
257 gchar *cmd_stdout = NULL;
259 GString *buf = g_string_new("");
260 GString *js_ret = g_string_new("");
265 g_string_append_c(buf, *++s);
270 etype = get_exp_type(s);
275 vend = strpbrk(s, end_simple_var);
276 if(!vend) vend = strchr(s, '\0');
280 vend = strchr(s, '}');
281 if(!vend) vend = strchr(s, '\0');
285 vend = strstr(s, ")@");
286 if(!vend) vend = strchr(s, '\0');
290 vend = strstr(s, ">@");
291 if(!vend) vend = strchr(s, '\0');
295 vend = strstr(s, "]@");
296 if(!vend) vend = strchr(s, '\0');
301 ret = g_strndup(s, vend-s);
305 if(etype == EXP_SIMPLE_VAR ||
306 etype == EXP_BRACED_VAR) {
307 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
308 if(c->type == TYPE_STR && *c->ptr != NULL) {
309 g_string_append(buf, (gchar *)*c->ptr);
310 } else if(c->type == TYPE_INT) {
311 g_string_append_printf(buf, "%d", (int)*c->ptr);
313 else if(c->type == TYPE_FLOAT) {
314 g_string_append_printf(buf, "%f", *(float *)c->ptr);
318 if(etype == EXP_SIMPLE_VAR)
323 else if(recurse != 1 &&
325 mycmd = expand(ret, 1);
326 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
330 g_printerr("error on running command: %s\n", err->message);
333 else if (*cmd_stdout) {
334 size_t len = strlen(cmd_stdout);
336 if(len > 0 && cmd_stdout[len-1] == '\n')
337 cmd_stdout[--len] = '\0'; /* strip trailing newline */
339 g_string_append(buf, cmd_stdout);
344 else if(recurse != 2 &&
346 mycmd = expand(ret, 2);
347 eval_js(uzbl.gui.web_view, mycmd, js_ret);
351 g_string_append(buf, js_ret->str);
352 g_string_free(js_ret, TRUE);
353 js_ret = g_string_new("");
357 else if(etype == EXP_ESCAPE) {
358 mycmd = expand(ret, 0);
359 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
361 g_string_append(buf, escaped);
373 g_string_append_c(buf, *s);
378 g_string_free(js_ret, TRUE);
379 return g_string_free(buf, FALSE);
386 snprintf(tmp, sizeof(tmp), "%i", val);
387 return g_strdup(tmp);
391 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
394 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
397 str_replace (const char* search, const char* replace, const char* string) {
401 buf = g_strsplit (string, search, -1);
402 ret = g_strjoinv (replace, buf);
403 g_strfreev(buf); // somebody said this segfaults
409 read_file_by_line (gchar *path) {
410 GIOChannel *chan = NULL;
411 gchar *readbuf = NULL;
413 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
416 chan = g_io_channel_new_file(path, "r", NULL);
419 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
420 const gchar* val = g_strdup (readbuf);
421 g_array_append_val (lines, val);
426 g_io_channel_unref (chan);
428 fprintf(stderr, "File '%s' not be read.\n", path);
435 parseenv (char* string) {
436 extern char** environ;
437 gchar* tmpstr = NULL;
441 while (environ[i] != NULL) {
442 gchar** env = g_strsplit (environ[i], "=", 2);
443 gchar* envname = g_strconcat ("$", env[0], NULL);
445 if (g_strrstr (string, envname) != NULL) {
446 tmpstr = g_strdup(string);
448 string = str_replace(envname, env[1], tmpstr);
453 g_strfreev (env); // somebody said this breaks uzbl
461 setup_signal(int signr, sigfunc *shandler) {
462 struct sigaction nh, oh;
464 nh.sa_handler = shandler;
465 sigemptyset(&nh.sa_mask);
468 if(sigaction(signr, &nh, &oh) < 0)
476 if (uzbl.behave.fifo_dir)
477 unlink (uzbl.comm.fifo_path);
478 if (uzbl.behave.socket_dir)
479 unlink (uzbl.comm.socket_path);
481 g_free(uzbl.state.executable_path);
482 g_free(uzbl.state.keycmd);
483 g_hash_table_destroy(uzbl.bindings);
484 g_hash_table_destroy(uzbl.behave.commands);
487 /* used for html_mode_timeout
488 * be sure to extend this function to use
489 * more timers if needed in other places
492 set_timeout(int seconds) {
494 memset(&t, 0, sizeof t);
496 t.it_value.tv_sec = seconds;
497 t.it_value.tv_usec = 0;
498 setitimer(ITIMER_REAL, &t, NULL);
501 /* --- SIGNAL HANDLER --- */
504 catch_sigterm(int s) {
510 catch_sigint(int s) {
520 set_var_value("mode", "0");
525 /* --- CALLBACKS --- */
528 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
531 (void) navigation_action;
532 (void) policy_decision;
534 const gchar* uri = webkit_network_request_get_uri (request);
535 if (uzbl.state.verbose)
536 printf("New window requested -> %s \n", uri);
537 webkit_web_policy_decision_use(policy_decision);
542 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
547 /* If we can display it, let's display it... */
548 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
549 webkit_web_policy_decision_use (policy_decision);
553 /* ...everything we can't displayed is downloaded */
554 webkit_web_policy_decision_download (policy_decision);
559 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
563 if (uzbl.state.selected_url != NULL) {
564 if (uzbl.state.verbose)
565 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
566 new_window_load_uri(uzbl.state.selected_url);
568 if (uzbl.state.verbose)
569 printf("New web view -> %s\n","Nothing to open, exiting");
575 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
578 if (uzbl.behave.download_handler) {
579 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
580 if (uzbl.state.verbose)
581 printf("Download -> %s\n",uri);
582 /* if urls not escaped, we may have to escape and quote uri before this call */
583 run_handler(uzbl.behave.download_handler, uri);
588 /* scroll a bar in a given direction */
590 scroll (GtkAdjustment* bar, GArray *argv) {
594 gdouble page_size = gtk_adjustment_get_page_size(bar);
595 gdouble value = gtk_adjustment_get_value(bar);
596 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
599 value += page_size * amount * 0.01;
603 max_value = gtk_adjustment_get_upper(bar) - page_size;
605 if (value > max_value)
606 value = max_value; /* don't scroll past the end of the page */
608 gtk_adjustment_set_value (bar, value);
612 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
613 (void) page; (void) argv; (void) result;
614 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
618 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
619 (void) page; (void) argv; (void) result;
620 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
621 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
625 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
626 (void) page; (void) result;
627 scroll(uzbl.gui.bar_v, argv);
631 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
632 (void) page; (void) result;
633 scroll(uzbl.gui.bar_h, argv);
638 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
639 if(uzbl.state.verbose)
640 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
642 /* update geometry var with the actual geometry
643 this is necessary as some WMs don't seem to honour
644 the above setting and we don't want to end up with
645 wrong geometry information
652 if (!uzbl.behave.show_status) {
653 gtk_widget_hide(uzbl.gui.mainbar);
655 gtk_widget_show(uzbl.gui.mainbar);
661 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
666 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
670 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
675 if (uzbl.behave.show_status) {
676 gtk_widget_hide(uzbl.gui.mainbar);
678 gtk_widget_show(uzbl.gui.mainbar);
680 uzbl.behave.show_status = !uzbl.behave.show_status;
685 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
689 //Set selected_url state variable
690 g_free(uzbl.state.selected_url);
691 uzbl.state.selected_url = NULL;
693 uzbl.state.selected_url = g_strdup(link);
699 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
702 const gchar *title = webkit_web_view_get_title(web_view);
703 if (uzbl.gui.main_title)
704 g_free (uzbl.gui.main_title);
705 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
710 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
713 uzbl.gui.sbar.load_progress = progress;
715 g_free(uzbl.gui.sbar.progress_bar);
716 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
722 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
726 if (uzbl.behave.load_finish_handler)
727 run_handler(uzbl.behave.load_finish_handler, "");
730 void clear_keycmd() {
731 g_free(uzbl.state.keycmd);
732 uzbl.state.keycmd = g_strdup("");
736 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
740 uzbl.gui.sbar.load_progress = 0;
741 clear_keycmd(); // don't need old commands to remain on new page?
742 if (uzbl.behave.load_start_handler)
743 run_handler(uzbl.behave.load_start_handler, "");
747 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
750 g_free (uzbl.state.uri);
751 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
752 uzbl.state.uri = g_string_free (newuri, FALSE);
753 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
754 set_insert_mode(uzbl.behave.always_insert_mode);
757 if (uzbl.behave.load_commit_handler)
758 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
762 destroy_cb (GtkWidget* widget, gpointer data) {
770 if (uzbl.behave.history_handler) {
772 struct tm * timeinfo;
775 timeinfo = localtime ( &rawtime );
776 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
777 run_handler(uzbl.behave.history_handler, date);
782 /* VIEW funcs (little webkit wrappers) */
783 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
785 VIEWFUNC(reload_bypass_cache)
786 VIEWFUNC(stop_loading)
793 /* -- command to callback/function map for things we cannot attach to any signals */
794 struct {char *key; CommandInfo value;} cmdlist[] =
795 { /* key function no_split */
796 { "back", {view_go_back, 0} },
797 { "forward", {view_go_forward, 0} },
798 { "scroll_vert", {scroll_vert, 0} },
799 { "scroll_horz", {scroll_horz, 0} },
800 { "scroll_begin", {scroll_begin, 0} },
801 { "scroll_end", {scroll_end, 0} },
802 { "reload", {view_reload, 0}, },
803 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
804 { "stop", {view_stop_loading, 0}, },
805 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
806 { "zoom_out", {view_zoom_out, 0}, },
807 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
808 { "uri", {load_uri, TRUE} },
809 { "js", {run_js, TRUE} },
810 { "script", {run_external_js, 0} },
811 { "toggle_status", {toggle_status_cb, 0} },
812 { "spawn", {spawn, 0} },
813 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
814 { "sh", {spawn_sh, 0} },
815 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
816 { "talk_to_socket", {talk_to_socket, 0} },
817 { "exit", {close_uzbl, 0} },
818 { "search", {search_forward_text, TRUE} },
819 { "search_reverse", {search_reverse_text, TRUE} },
820 { "dehilight", {dehilight, 0} },
821 { "toggle_insert_mode", {toggle_insert_mode, 0} },
822 { "set", {set_var, TRUE} },
823 //{ "get", {get_var, TRUE} },
824 { "bind", {act_bind, TRUE} },
825 { "dump_config", {act_dump_config, 0} },
826 { "keycmd", {keycmd, TRUE} },
827 { "keycmd_nl", {keycmd_nl, TRUE} },
828 { "keycmd_bs", {keycmd_bs, 0} },
829 { "chain", {chain, 0} },
830 { "print", {print, TRUE} },
831 { "update_gui", {update_gui, TRUE} }
838 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
840 for (i = 0; i < LENGTH(cmdlist); i++)
841 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
844 /* -- CORE FUNCTIONS -- */
847 free_action(gpointer act) {
848 Action *action = (Action*)act;
849 g_free(action->name);
851 g_free(action->param);
856 new_action(const gchar *name, const gchar *param) {
857 Action *action = g_new(Action, 1);
859 action->name = g_strdup(name);
861 action->param = g_strdup(param);
863 action->param = NULL;
869 file_exists (const char * filename) {
870 return (access(filename, F_OK) == 0);
874 set_var(WebKitWebView *page, GArray *argv, GString *result) {
875 (void) page; (void) result;
876 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
877 if (split[0] != NULL) {
878 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
879 set_var_value(g_strstrip(split[0]), value);
886 update_gui(WebKitWebView *page, GArray *argv, GString *result) {
887 (void) page; (void) argv; (void) result;
893 print(WebKitWebView *page, GArray *argv, GString *result) {
894 (void) page; (void) result;
897 buf = expand(argv_idx(argv, 0), 0);
898 g_string_assign(result, buf);
903 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
904 (void) page; (void) result;
905 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
906 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
907 add_binding(g_strstrip(split[0]), value);
925 set_mode_indicator() {
926 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
927 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
932 set_mode_indicator();
937 set_insert_mode(gboolean mode) {
938 uzbl.behave.insert_mode = mode;
939 set_mode_indicator();
943 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
944 (void) page; (void) result;
946 if (argv_idx(argv, 0)) {
947 if (strcmp (argv_idx(argv, 0), "0") == 0) {
948 set_insert_mode(FALSE);
950 set_insert_mode(TRUE);
953 set_insert_mode( !uzbl.behave.insert_mode );
960 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
963 if (argv_idx(argv, 0)) {
964 GString* newuri = g_string_new (argv_idx(argv, 0));
965 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
966 run_js(web_view, argv, NULL);
969 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
970 g_string_prepend (newuri, "http://");
971 /* if we do handle cookies, ask our handler for them */
972 webkit_web_view_load_uri (web_view, newuri->str);
973 g_string_free (newuri, TRUE);
980 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
981 size_t argumentCount, const JSValueRef arguments[],
982 JSValueRef* exception) {
987 JSStringRef js_result_string;
988 GString *result = g_string_new("");
990 if (argumentCount >= 1) {
991 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
992 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
993 char ctl_line[arg_size];
994 JSStringGetUTF8CString(arg, ctl_line, arg_size);
996 parse_cmd_line(ctl_line, result);
998 JSStringRelease(arg);
1000 js_result_string = JSStringCreateWithUTF8CString(result->str);
1002 g_string_free(result, TRUE);
1004 return JSValueMakeString(ctx, js_result_string);
1007 JSStaticFunction js_static_functions[] = {
1008 {"run", js_run_command, kJSPropertyAttributeNone},
1013 /* This function creates the class and its definition, only once */
1014 if (!uzbl.js.initialized) {
1015 /* it would be pretty cool to make this dynamic */
1016 uzbl.js.classdef = kJSClassDefinitionEmpty;
1017 uzbl.js.classdef.staticFunctions = js_static_functions;
1019 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1025 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1026 WebKitWebFrame *frame;
1027 JSGlobalContextRef context;
1028 JSObjectRef globalobject;
1029 JSStringRef var_name;
1031 JSStringRef js_script;
1032 JSValueRef js_result;
1033 JSStringRef js_result_string;
1034 size_t js_result_size;
1038 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1039 context = webkit_web_frame_get_global_context(frame);
1040 globalobject = JSContextGetGlobalObject(context);
1042 /* uzbl javascript namespace */
1043 var_name = JSStringCreateWithUTF8CString("Uzbl");
1044 JSObjectSetProperty(context, globalobject, var_name,
1045 JSObjectMake(context, uzbl.js.classref, NULL),
1046 kJSClassAttributeNone, NULL);
1048 /* evaluate the script and get return value*/
1049 js_script = JSStringCreateWithUTF8CString(script);
1050 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1051 if (js_result && !JSValueIsUndefined(context, js_result)) {
1052 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1053 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1055 if (js_result_size) {
1056 char js_result_utf8[js_result_size];
1057 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1058 g_string_assign(result, js_result_utf8);
1061 JSStringRelease(js_result_string);
1065 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1067 JSStringRelease(var_name);
1068 JSStringRelease(js_script);
1072 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1073 if (argv_idx(argv, 0))
1074 eval_js(web_view, argv_idx(argv, 0), result);
1078 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1080 if (argv_idx(argv, 0)) {
1081 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1086 while ((line = g_array_index(lines, gchar*, i))) {
1088 js = g_strdup (line);
1090 gchar* newjs = g_strconcat (js, line, NULL);
1097 if (uzbl.state.verbose)
1098 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1100 if (argv_idx (argv, 1)) {
1101 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1105 eval_js (web_view, js, result);
1107 g_array_free (lines, TRUE);
1112 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1113 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1114 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1115 webkit_web_view_unmark_text_matches (page);
1116 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1117 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1121 if (uzbl.state.searchtx) {
1122 if (uzbl.state.verbose)
1123 printf ("Searching: %s\n", uzbl.state.searchtx);
1124 webkit_web_view_set_highlight_text_matches (page, TRUE);
1125 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1130 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1132 search_text(page, argv, TRUE);
1136 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1138 search_text(page, argv, FALSE);
1142 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1143 (void) argv; (void) result;
1144 webkit_web_view_set_highlight_text_matches (page, FALSE);
1149 new_window_load_uri (const gchar * uri) {
1150 if (uzbl.behave.new_window) {
1151 GString *s = g_string_new ("");
1152 g_string_printf(s, "'%s'", uri);
1153 run_handler(uzbl.behave.new_window, s->str);
1156 GString* to_execute = g_string_new ("");
1157 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1159 for (i = 0; entries[i].long_name != NULL; i++) {
1160 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1161 gchar** str = (gchar**)entries[i].arg_data;
1163 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1167 if (uzbl.state.verbose)
1168 printf("\n%s\n", to_execute->str);
1169 g_spawn_command_line_async (to_execute->str, NULL);
1170 g_string_free (to_execute, TRUE);
1174 chain (WebKitWebView *page, GArray *argv, GString *result) {
1175 (void) page; (void) result;
1177 gchar **parts = NULL;
1179 while ((a = argv_idx(argv, i++))) {
1180 parts = g_strsplit (a, " ", 2);
1182 parse_command(parts[0], parts[1], result);
1188 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1192 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1198 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1202 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1208 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1213 int len = strlen(uzbl.state.keycmd);
1214 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1216 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1221 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1228 /* --Statusbar functions-- */
1230 build_progressbar_ascii(int percent) {
1231 int width=uzbl.gui.sbar.progress_w;
1234 GString *bar = g_string_new("");
1236 l = (double)percent*((double)width/100.);
1237 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1239 for(i=0; i<(int)l; i++)
1240 g_string_append(bar, uzbl.gui.sbar.progress_s);
1243 g_string_append(bar, uzbl.gui.sbar.progress_u);
1245 return g_string_free(bar, FALSE);
1247 /* --End Statusbar functions-- */
1250 sharg_append(GArray *a, const gchar *str) {
1251 const gchar *s = (str ? str : "");
1252 g_array_append_val(a, s);
1255 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1257 run_command (const gchar *command, const guint npre, const gchar **args,
1258 const gboolean sync, char **output_stdout) {
1259 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1262 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1263 gchar *pid = itos(getpid());
1264 gchar *xwin = itos(uzbl.xwin);
1266 sharg_append(a, command);
1267 for (i = 0; i < npre; i++) /* add n args before the default vars */
1268 sharg_append(a, args[i]);
1269 sharg_append(a, uzbl.state.config_file);
1270 sharg_append(a, pid);
1271 sharg_append(a, xwin);
1272 sharg_append(a, uzbl.comm.fifo_path);
1273 sharg_append(a, uzbl.comm.socket_path);
1274 sharg_append(a, uzbl.state.uri);
1275 sharg_append(a, uzbl.gui.main_title);
1277 for (i = npre; i < g_strv_length((gchar**)args); i++)
1278 sharg_append(a, args[i]);
1282 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1284 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1285 NULL, NULL, output_stdout, NULL, NULL, &err);
1286 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1287 NULL, NULL, NULL, &err);
1289 if (uzbl.state.verbose) {
1290 GString *s = g_string_new("spawned:");
1291 for (i = 0; i < (a->len); i++) {
1292 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1293 g_string_append_printf(s, " %s", qarg);
1296 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1297 printf("%s\n", s->str);
1298 g_string_free(s, TRUE);
1300 printf("Stdout: %s\n", *output_stdout);
1304 g_printerr("error on run_command: %s\n", err->message);
1309 g_array_free (a, TRUE);
1314 split_quoted(const gchar* src, const gboolean unquote) {
1315 /* split on unquoted space, return array of strings;
1316 remove a layer of quotes and backslashes if unquote */
1317 if (!src) return NULL;
1319 gboolean dq = FALSE;
1320 gboolean sq = FALSE;
1321 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1322 GString *s = g_string_new ("");
1326 for (p = src; *p != '\0'; p++) {
1327 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1328 else if (*p == '\\') { g_string_append_c(s, *p++);
1329 g_string_append_c(s, *p); }
1330 else if ((*p == '"') && unquote && !sq) dq = !dq;
1331 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1333 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1334 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1336 else if ((*p == ' ') && !dq && !sq) {
1337 dup = g_strdup(s->str);
1338 g_array_append_val(a, dup);
1339 g_string_truncate(s, 0);
1340 } else g_string_append_c(s, *p);
1342 dup = g_strdup(s->str);
1343 g_array_append_val(a, dup);
1344 ret = (gchar**)a->data;
1345 g_array_free (a, FALSE);
1346 g_string_free (s, TRUE);
1351 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1352 (void)web_view; (void)result;
1353 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1354 if (argv_idx(argv, 0))
1355 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1359 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1360 (void)web_view; (void)result;
1362 if (argv_idx(argv, 0))
1363 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1364 TRUE, &uzbl.comm.sync_stdout);
1368 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1369 (void)web_view; (void)result;
1370 if (!uzbl.behave.shell_cmd) {
1371 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1376 gchar *spacer = g_strdup("");
1377 g_array_insert_val(argv, 1, spacer);
1378 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1380 for (i = 1; i < g_strv_length(cmd); i++)
1381 g_array_prepend_val(argv, cmd[i]);
1383 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1389 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1390 (void)web_view; (void)result;
1391 if (!uzbl.behave.shell_cmd) {
1392 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1397 gchar *spacer = g_strdup("");
1398 g_array_insert_val(argv, 1, spacer);
1399 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1401 for (i = 1; i < g_strv_length(cmd); i++)
1402 g_array_prepend_val(argv, cmd[i]);
1404 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1405 TRUE, &uzbl.comm.sync_stdout);
1411 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1412 (void)web_view; (void)result;
1415 struct sockaddr_un sa;
1422 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1424 /* This function could be optimised by storing a hash table of socket paths
1425 and associated connected file descriptors rather than closing and
1426 re-opening for every call. Also we could launch a script if socket connect
1429 /* First element argv[0] is path to socket. Following elements are tokens to
1430 write to the socket. We write them as a single packet with each token
1431 separated by an ASCII nul (\0). */
1433 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1438 /* copy socket path, null terminate result */
1439 sockpath = g_array_index(argv, char*, 0);
1440 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1441 sa.sun_family = AF_UNIX;
1443 /* create socket file descriptor and connect it to path */
1444 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1446 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1449 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1450 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1455 /* build request vector */
1456 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1458 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1462 for(i = 1; i < argv->len; ++i) {
1463 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1464 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1468 ret = writev(fd, iov, argv->len - 1);
1471 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1476 /* wait for a response, with a 500ms timeout */
1478 pfd.events = POLLIN;
1480 ret = poll(&pfd, 1, 500);
1482 if(ret == 0) errno = ETIMEDOUT;
1483 if(errno == EINTR) continue;
1484 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1490 /* get length of response */
1491 if(ioctl(fd, FIONREAD, &len) == -1) {
1492 g_printerr("talk_to_socket: cannot find daemon response length, "
1493 "ioctl failed (%s)\n", strerror(errno));
1498 /* if there is a response, read it */
1500 uzbl.comm.sync_stdout = g_malloc(len + 1);
1501 if(!uzbl.comm.sync_stdout) {
1502 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1506 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1508 ret = read(fd, uzbl.comm.sync_stdout, len);
1510 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1523 parse_command(const char *cmd, const char *param, GString *result) {
1526 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1528 gchar **par = split_quoted(param, TRUE);
1529 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1531 if (c->no_split) { /* don't split */
1532 sharg_append(a, param);
1534 for (i = 0; i < g_strv_length(par); i++)
1535 sharg_append(a, par[i]);
1538 if (result == NULL) {
1539 GString *result_print = g_string_new("");
1541 c->function(uzbl.gui.web_view, a, result_print);
1542 if (result_print->len)
1543 printf("%*s\n", result_print->len, result_print->str);
1545 g_string_free(result_print, TRUE);
1547 c->function(uzbl.gui.web_view, a, result);
1550 g_array_free (a, TRUE);
1553 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1560 if(*uzbl.net.proxy_url == ' '
1561 || uzbl.net.proxy_url == NULL) {
1562 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1563 (GType) SOUP_SESSION_PROXY_URI);
1566 suri = soup_uri_new(uzbl.net.proxy_url);
1567 g_object_set(G_OBJECT(uzbl.net.soup_session),
1568 SOUP_SESSION_PROXY_URI,
1570 soup_uri_free(suri);
1577 if(file_exists(uzbl.gui.icon)) {
1578 if (uzbl.gui.main_window)
1579 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1581 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1587 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1588 g_array_append_val (a, uzbl.state.uri);
1589 load_uri(uzbl.gui.web_view, a, NULL);
1590 g_array_free (a, TRUE);
1594 cmd_always_insert_mode() {
1595 set_insert_mode(uzbl.behave.always_insert_mode);
1601 g_object_set(G_OBJECT(uzbl.net.soup_session),
1602 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1606 cmd_max_conns_host() {
1607 g_object_set(G_OBJECT(uzbl.net.soup_session),
1608 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1613 soup_session_remove_feature
1614 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1615 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1616 /*g_free(uzbl.net.soup_logger);*/
1618 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1619 soup_session_add_feature(uzbl.net.soup_session,
1620 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1625 return webkit_web_view_get_settings(uzbl.gui.web_view);
1630 WebKitWebSettings *ws = view_settings();
1631 if (uzbl.behave.font_size > 0) {
1632 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1635 if (uzbl.behave.monospace_size > 0) {
1636 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1637 uzbl.behave.monospace_size, NULL);
1639 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1640 uzbl.behave.font_size, NULL);
1645 cmd_default_font_family() {
1646 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1647 uzbl.behave.default_font_family, NULL);
1651 cmd_monospace_font_family() {
1652 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1653 uzbl.behave.monospace_font_family, NULL);
1657 cmd_sans_serif_font_family() {
1658 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1659 uzbl.behave.sans_serif_font_family, NULL);
1663 cmd_serif_font_family() {
1664 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1665 uzbl.behave.serif_font_family, NULL);
1669 cmd_cursive_font_family() {
1670 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1671 uzbl.behave.cursive_font_family, NULL);
1675 cmd_fantasy_font_family() {
1676 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1677 uzbl.behave.fantasy_font_family, NULL);
1682 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1686 cmd_disable_plugins() {
1687 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1688 !uzbl.behave.disable_plugins, NULL);
1692 cmd_disable_scripts() {
1693 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1694 !uzbl.behave.disable_scripts, NULL);
1698 cmd_minimum_font_size() {
1699 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1700 uzbl.behave.minimum_font_size, NULL);
1703 cmd_autoload_img() {
1704 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1705 uzbl.behave.autoload_img, NULL);
1710 cmd_autoshrink_img() {
1711 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1712 uzbl.behave.autoshrink_img, NULL);
1717 cmd_enable_spellcheck() {
1718 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1719 uzbl.behave.enable_spellcheck, NULL);
1723 cmd_enable_private() {
1724 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1725 uzbl.behave.enable_private, NULL);
1730 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1731 uzbl.behave.print_bg, NULL);
1736 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1737 uzbl.behave.style_uri, NULL);
1741 cmd_resizable_txt() {
1742 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1743 uzbl.behave.resizable_txt, NULL);
1747 cmd_default_encoding() {
1748 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1749 uzbl.behave.default_encoding, NULL);
1753 cmd_enforce_96dpi() {
1754 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1755 uzbl.behave.enforce_96dpi, NULL);
1759 cmd_caret_browsing() {
1760 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1761 uzbl.behave.caret_browsing, NULL);
1765 cmd_cookie_handler() {
1766 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1767 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1768 if ((g_strcmp0(split[0], "sh") == 0) ||
1769 (g_strcmp0(split[0], "spawn") == 0)) {
1770 g_free (uzbl.behave.cookie_handler);
1771 uzbl.behave.cookie_handler =
1772 g_strdup_printf("sync_%s %s", split[0], split[1]);
1779 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1780 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1781 if ((g_strcmp0(split[0], "sh") == 0) ||
1782 (g_strcmp0(split[0], "spawn") == 0)) {
1783 g_free (uzbl.behave.new_window);
1784 uzbl.behave.new_window =
1785 g_strdup_printf("%s %s", split[0], split[1]);
1792 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1797 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1802 if(uzbl.behave.inject_html) {
1803 webkit_web_view_load_html_string (uzbl.gui.web_view,
1804 uzbl.behave.inject_html, NULL);
1813 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1814 uzbl.behave.modmask = 0;
1816 if(uzbl.behave.modkey)
1817 g_free(uzbl.behave.modkey);
1818 uzbl.behave.modkey = buf;
1820 for (i = 0; modkeys[i].key != NULL; i++) {
1821 if (g_strrstr(buf, modkeys[i].key))
1822 uzbl.behave.modmask |= modkeys[i].mask;
1828 if (*uzbl.net.useragent == ' ') {
1829 g_free (uzbl.net.useragent);
1830 uzbl.net.useragent = NULL;
1832 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1833 uzbl.net.useragent, NULL);
1839 if (!uzbl.gui.scrolled_win &&
1843 gtk_widget_ref(uzbl.gui.scrolled_win);
1844 gtk_widget_ref(uzbl.gui.mainbar);
1845 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1846 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1848 if(uzbl.behave.status_top) {
1849 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1850 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1853 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1854 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1856 gtk_widget_unref(uzbl.gui.scrolled_win);
1857 gtk_widget_unref(uzbl.gui.mainbar);
1858 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1863 set_var_value(gchar *name, gchar *val) {
1864 uzbl_cmdprop *c = NULL;
1867 char *invalid_chars = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
1869 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1870 if(!c->writeable) return FALSE;
1872 /* check for the variable type */
1873 if (c->type == TYPE_STR) {
1874 buf = expand(val, 0);
1877 } else if(c->type == TYPE_INT) {
1878 int *ip = (int *)c->ptr;
1879 buf = expand(val, 0);
1880 *ip = (int)strtoul(buf, &endp, 10);
1882 } else if (c->type == TYPE_FLOAT) {
1883 float *fp = (float *)c->ptr;
1884 buf = expand(val, 0);
1885 *fp = strtod(buf, &endp);
1889 /* invoke a command specific function */
1890 if(c->func) c->func();
1892 /* check wether name violates our naming scheme */
1893 if(strpbrk(name, invalid_chars)) {
1894 if (uzbl.state.verbose)
1895 printf("Invalid variable name\n");
1900 c = malloc(sizeof(uzbl_cmdprop));
1905 buf = expand(val, 0);
1906 c->ptr = malloc(sizeof(char *));
1908 g_hash_table_insert(uzbl.comm.proto_var,
1909 g_strdup(name), (gpointer) c);
1916 Behaviour *b = &uzbl.behave;
1918 if(b->html_buffer->str) {
1919 webkit_web_view_load_html_string (uzbl.gui.web_view,
1920 b->html_buffer->str, b->base_url);
1921 g_string_free(b->html_buffer, TRUE);
1922 b->html_buffer = g_string_new("");
1926 enum {M_CMD, M_HTML};
1928 parse_cmd_line(const char *ctl_line, GString *result) {
1929 Behaviour *b = &uzbl.behave;
1932 if(b->mode == M_HTML) {
1933 len = strlen(b->html_endmarker);
1934 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1935 if(len == strlen(ctl_line)-1 &&
1936 !strncmp(b->html_endmarker, ctl_line, len)) {
1938 set_var_value("mode", "0");
1943 set_timeout(b->html_timeout);
1944 g_string_append(b->html_buffer, ctl_line);
1947 else if((ctl_line[0] == '#') /* Comments */
1948 || (ctl_line[0] == ' ')
1949 || (ctl_line[0] == '\n'))
1950 ; /* ignore these lines */
1951 else { /* parse a command */
1953 gchar **tokens = NULL;
1954 len = strlen(ctl_line);
1956 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1957 ctlstrip = g_strndup(ctl_line, len - 1);
1958 else ctlstrip = g_strdup(ctl_line);
1960 tokens = g_strsplit(ctlstrip, " ", 2);
1961 parse_command(tokens[0], tokens[1], result);
1968 build_stream_name(int type, const gchar* dir) {
1969 State *s = &uzbl.state;
1973 str = g_strdup_printf
1974 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1975 } else if (type == SOCKET) {
1976 str = g_strdup_printf
1977 ("%s/uzbl_socket_%s", dir, s->instance_name);
1983 control_fifo(GIOChannel *gio, GIOCondition condition) {
1984 if (uzbl.state.verbose)
1985 printf("triggered\n");
1990 if (condition & G_IO_HUP)
1991 g_error ("Fifo: Read end of pipe died!\n");
1994 g_error ("Fifo: GIOChannel broke\n");
1996 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1997 if (ret == G_IO_STATUS_ERROR) {
1998 g_error ("Fifo: Error reading: %s\n", err->message);
2002 parse_cmd_line(ctl_line, NULL);
2009 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2010 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
2011 if (unlink(uzbl.comm.fifo_path) == -1)
2012 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
2013 g_free(uzbl.comm.fifo_path);
2014 uzbl.comm.fifo_path = NULL;
2017 GIOChannel *chan = NULL;
2018 GError *error = NULL;
2019 gchar *path = build_stream_name(FIFO, dir);
2021 if (!file_exists(path)) {
2022 if (mkfifo (path, 0666) == 0) {
2023 // we don't really need to write to the file, but if we open the file as 'r' we will block here, waiting for a writer to open the file.
2024 chan = g_io_channel_new_file(path, "r+", &error);
2026 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
2027 if (uzbl.state.verbose)
2028 printf ("init_fifo: created successfully as %s\n", path);
2029 uzbl.comm.fifo_path = path;
2031 } else g_warning ("init_fifo: could not add watch on %s\n", path);
2032 } else g_warning ("init_fifo: can't open: %s\n", error->message);
2033 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
2034 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
2036 /* if we got this far, there was an error; cleanup */
2037 if (error) g_error_free (error);
2044 control_stdin(GIOChannel *gio, GIOCondition condition) {
2046 gchar *ctl_line = NULL;
2049 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
2050 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
2053 parse_cmd_line(ctl_line, NULL);
2061 GIOChannel *chan = NULL;
2062 GError *error = NULL;
2064 chan = g_io_channel_unix_new(fileno(stdin));
2066 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2067 g_error ("Stdin: could not add watch\n");
2069 if (uzbl.state.verbose)
2070 printf ("Stdin: watch added successfully\n");
2073 g_error ("Stdin: Error while opening: %s\n", error->message);
2075 if (error) g_error_free (error);
2079 control_socket(GIOChannel *chan) {
2080 struct sockaddr_un remote;
2081 unsigned int t = sizeof(remote);
2083 GIOChannel *clientchan;
2085 clientsock = accept (g_io_channel_unix_get_fd(chan),
2086 (struct sockaddr *) &remote, &t);
2088 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2089 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2090 (GIOFunc) control_client_socket, clientchan);
2097 control_client_socket(GIOChannel *clientchan) {
2099 GString *result = g_string_new("");
2100 GError *error = NULL;
2104 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2105 if (ret == G_IO_STATUS_ERROR) {
2106 g_warning ("Error reading: %s\n", error->message);
2107 g_io_channel_shutdown(clientchan, TRUE, &error);
2109 } else if (ret == G_IO_STATUS_EOF) {
2110 /* shutdown and remove channel watch from main loop */
2111 g_io_channel_shutdown(clientchan, TRUE, &error);
2116 parse_cmd_line (ctl_line, result);
2117 g_string_append_c(result, '\n');
2118 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2120 if (ret == G_IO_STATUS_ERROR) {
2121 g_warning ("Error writing: %s", error->message);
2123 g_io_channel_flush(clientchan, &error);
2126 if (error) g_error_free (error);
2127 g_string_free(result, TRUE);
2133 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2134 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2135 if (unlink(uzbl.comm.socket_path) == -1)
2136 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2137 g_free(uzbl.comm.socket_path);
2138 uzbl.comm.socket_path = NULL;
2146 GIOChannel *chan = NULL;
2148 struct sockaddr_un local;
2149 gchar *path = build_stream_name(SOCKET, dir);
2151 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2153 local.sun_family = AF_UNIX;
2154 strcpy (local.sun_path, path);
2155 unlink (local.sun_path);
2157 len = strlen (local.sun_path) + sizeof (local.sun_family);
2158 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2159 if (uzbl.state.verbose)
2160 printf ("init_socket: opened in %s\n", path);
2163 if( (chan = g_io_channel_unix_new(sock)) ) {
2164 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2165 uzbl.comm.socket_path = path;
2168 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2170 /* if we got this far, there was an error; cleanup */
2177 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2178 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2180 // this function may be called very early when the templates are not set (yet), hence the checks
2182 update_title (void) {
2183 Behaviour *b = &uzbl.behave;
2186 if (b->show_status) {
2187 if (b->title_format_short) {
2188 parsed = expand(b->title_format_short, 0);
2189 if (uzbl.gui.main_window)
2190 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2193 if (b->status_format) {
2194 parsed = expand(b->status_format, 0);
2195 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2198 if (b->status_background) {
2200 gdk_color_parse (b->status_background, &color);
2201 //labels and hboxes do not draw their own background. applying this on the vbox/main_window is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2202 if (uzbl.gui.main_window)
2203 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2204 else if (uzbl.gui.plug)
2205 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2208 if (b->title_format_long) {
2209 parsed = expand(b->title_format_long, 0);
2210 if (uzbl.gui.main_window)
2211 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2218 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2222 retrieve_geometry();
2227 key_press_cb (GtkWidget* window, GdkEventKey* event)
2229 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2233 if (event->type != GDK_KEY_PRESS ||
2234 event->keyval == GDK_Page_Up ||
2235 event->keyval == GDK_Page_Down ||
2236 event->keyval == GDK_Up ||
2237 event->keyval == GDK_Down ||
2238 event->keyval == GDK_Left ||
2239 event->keyval == GDK_Right ||
2240 event->keyval == GDK_Shift_L ||
2241 event->keyval == GDK_Shift_R)
2244 /* turn off insert mode (if always_insert_mode is not used) */
2245 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2246 set_insert_mode(uzbl.behave.always_insert_mode);
2251 if (uzbl.behave.insert_mode &&
2252 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2253 (!uzbl.behave.modmask)
2258 if (event->keyval == GDK_Escape) {
2261 dehilight(uzbl.gui.web_view, NULL, NULL);
2265 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2266 if (event->keyval == GDK_Insert) {
2268 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2269 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2271 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2274 GString* keycmd = g_string_new(uzbl.state.keycmd);
2275 g_string_append (keycmd, str);
2276 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2283 if (event->keyval == GDK_BackSpace)
2284 keycmd_bs(NULL, NULL, NULL);
2286 gboolean key_ret = FALSE;
2287 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2290 GString* keycmd = g_string_new(uzbl.state.keycmd);
2291 g_string_append(keycmd, event->string);
2292 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2295 run_keycmd(key_ret);
2297 if (key_ret) return (!uzbl.behave.insert_mode);
2302 run_keycmd(const gboolean key_ret) {
2303 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2305 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2307 parse_command(act->name, act->param, NULL);
2311 /* try if it's an incremental keycmd or one that takes args, and run it */
2312 GString* short_keys = g_string_new ("");
2313 GString* short_keys_inc = g_string_new ("");
2315 guint len = strlen(uzbl.state.keycmd);
2316 for (i=0; i<len; i++) {
2317 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2318 g_string_assign(short_keys_inc, short_keys->str);
2319 g_string_append_c(short_keys, '_');
2320 g_string_append_c(short_keys_inc, '*');
2322 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2323 /* run normal cmds only if return was pressed */
2324 exec_paramcmd(act, i);
2327 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2328 if (key_ret) /* just quit the incremental command on return */
2330 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2334 g_string_truncate(short_keys, short_keys->len - 1);
2336 g_string_free (short_keys, TRUE);
2337 g_string_free (short_keys_inc, TRUE);
2341 exec_paramcmd(const Action *act, const guint i) {
2342 GString *parampart = g_string_new (uzbl.state.keycmd);
2343 GString *actionname = g_string_new ("");
2344 GString *actionparam = g_string_new ("");
2345 g_string_erase (parampart, 0, i+1);
2347 g_string_printf (actionname, act->name, parampart->str);
2349 g_string_printf (actionparam, act->param, parampart->str);
2350 parse_command(actionname->str, actionparam->str, NULL);
2351 g_string_free(actionname, TRUE);
2352 g_string_free(actionparam, TRUE);
2353 g_string_free(parampart, TRUE);
2361 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2363 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2364 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2365 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2366 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2367 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2368 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2369 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2370 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2371 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2372 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2373 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2380 g->mainbar = gtk_hbox_new (FALSE, 0);
2382 /* keep a reference to the bar so we can re-pack it at runtime*/
2383 //sbar_ref = g_object_ref(g->mainbar);
2385 g->mainbar_label = gtk_label_new ("");
2386 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2387 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2388 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2389 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2390 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2391 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2397 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2398 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2399 gtk_widget_set_name (window, "Uzbl browser");
2400 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2401 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2402 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2409 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2410 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2411 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2418 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2420 If actname is one that calls an external command, this function will inject
2421 newargs in front of the user-provided args in that command line. They will
2422 come become after the body of the script (in sh) or after the name of
2423 the command to execute (in spawn).
2424 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2425 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2427 The return value consist of two strings: the action (sh, ...) and its args.
2429 If act is not one that calls an external command, then the given action merely
2432 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2433 /* Arrr! Here be memory leaks */
2434 gchar *actdup = g_strdup(actname);
2435 g_array_append_val(rets, actdup);
2437 if ((g_strcmp0(actname, "spawn") == 0) ||
2438 (g_strcmp0(actname, "sh") == 0) ||
2439 (g_strcmp0(actname, "sync_spawn") == 0) ||
2440 (g_strcmp0(actname, "sync_sh") == 0) ||
2441 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2443 GString *a = g_string_new("");
2444 gchar **spawnparts = split_quoted(origargs, FALSE);
2445 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2446 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2448 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2449 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2451 g_array_append_val(rets, a->str);
2452 g_string_free(a, FALSE);
2453 g_strfreev(spawnparts);
2455 gchar *origdup = g_strdup(origargs);
2456 g_array_append_val(rets, origdup);
2458 return (gchar**)g_array_free(rets, FALSE);
2462 run_handler (const gchar *act, const gchar *args) {
2463 /* Consider this code a temporary hack to make the handlers usable.
2464 In practice, all this splicing, injection, and reconstruction is
2465 inefficient, annoying and hard to manage. Potential pitfalls arise
2466 when the handler specific args 1) are not quoted (the handler
2467 callbacks should take care of this) 2) are quoted but interfere
2468 with the users' own quotation. A more ideal solution is
2469 to refactor parse_command so that it doesn't just take a string
2470 and execute it; rather than that, we should have a function which
2471 returns the argument vector parsed from the string. This vector
2472 could be modified (e.g. insert additional args into it) before
2473 passing it to the next function that actually executes it. Though
2474 it still isn't perfect for chain actions.. will reconsider & re-
2475 factor when I have the time. -duc */
2477 char **parts = g_strsplit(act, " ", 2);
2479 if (g_strcmp0(parts[0], "chain") == 0) {
2480 GString *newargs = g_string_new("");
2481 gchar **chainparts = split_quoted(parts[1], FALSE);
2483 /* for every argument in the chain, inject the handler args
2484 and make sure the new parts are wrapped in quotes */
2485 gchar **cp = chainparts;
2487 gchar *quotless = NULL;
2488 gchar **spliced_quotless = NULL; // sigh -_-;
2489 gchar **inpart = NULL;
2492 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2494 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2495 } else quotless = g_strdup(*cp);
2497 spliced_quotless = g_strsplit(quotless, " ", 2);
2498 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2499 g_strfreev(spliced_quotless);
2501 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2507 parse_command(parts[0], &(newargs->str[1]), NULL);
2508 g_string_free(newargs, TRUE);
2509 g_strfreev(chainparts);
2512 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2513 parse_command(inparts[0], inparts[1], NULL);
2521 add_binding (const gchar *key, const gchar *act) {
2522 char **parts = g_strsplit(act, " ", 2);
2529 if (uzbl.state.verbose)
2530 printf ("Binding %-10s : %s\n", key, act);
2531 action = new_action(parts[0], parts[1]);
2533 if (g_hash_table_remove (uzbl.bindings, key))
2534 g_warning ("Overwriting existing binding for \"%s\"", key);
2535 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2540 get_xdg_var (XDG_Var xdg) {
2541 const gchar* actual_value = getenv (xdg.environmental);
2542 const gchar* home = getenv ("HOME");
2543 gchar* return_value;
2545 if (! actual_value || strcmp (actual_value, "") == 0) {
2546 if (xdg.default_value) {
2547 return_value = str_replace ("~", home, xdg.default_value);
2549 return_value = NULL;
2552 return_value = str_replace("~", home, actual_value);
2555 return return_value;
2559 find_xdg_file (int xdg_type, char* filename) {
2560 /* xdg_type = 0 => config
2561 xdg_type = 1 => data
2562 xdg_type = 2 => cache*/
2564 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2565 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2568 gchar* temporary_string;
2572 if (! file_exists (temporary_file) && xdg_type != 2) {
2573 buf = get_xdg_var (XDG[3 + xdg_type]);
2574 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2577 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2578 g_free (temporary_file);
2579 temporary_file = g_strconcat (temporary_string, filename, NULL);
2583 //g_free (temporary_string); - segfaults.
2585 if (file_exists (temporary_file)) {
2586 return temporary_file;
2593 State *s = &uzbl.state;
2594 Network *n = &uzbl.net;
2596 for (i = 0; default_config[i].command != NULL; i++) {
2597 parse_cmd_line(default_config[i].command, NULL);
2600 if (g_strcmp0(s->config_file, "-") == 0) {
2601 s->config_file = NULL;
2605 else if (!s->config_file) {
2606 s->config_file = find_xdg_file (0, "/uzbl/config");
2609 if (s->config_file) {
2610 GArray* lines = read_file_by_line (s->config_file);
2614 while ((line = g_array_index(lines, gchar*, i))) {
2615 parse_cmd_line (line, NULL);
2619 g_array_free (lines, TRUE);
2621 if (uzbl.state.verbose)
2622 printf ("No configuration file loaded.\n");
2625 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2628 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2631 if (!uzbl.behave.cookie_handler)
2634 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2635 GString *s = g_string_new ("");
2636 SoupURI * soup_uri = soup_message_get_uri(msg);
2637 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2638 run_handler(uzbl.behave.cookie_handler, s->str);
2640 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2641 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2642 if ( p != NULL ) *p = '\0';
2643 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2645 if (uzbl.comm.sync_stdout)
2646 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2648 g_string_free(s, TRUE);
2652 save_cookies (SoupMessage *msg, gpointer user_data){
2656 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2657 cookie = soup_cookie_to_set_cookie_header(ck->data);
2658 SoupURI * soup_uri = soup_message_get_uri(msg);
2659 GString *s = g_string_new ("");
2660 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2661 run_handler(uzbl.behave.cookie_handler, s->str);
2663 g_string_free(s, TRUE);
2668 /* --- WEBINSPECTOR --- */
2670 hide_window_cb(GtkWidget *widget, gpointer data) {
2673 gtk_widget_hide(widget);
2677 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2680 (void) web_inspector;
2681 GtkWidget* scrolled_window;
2682 GtkWidget* new_web_view;
2685 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2686 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2687 G_CALLBACK(hide_window_cb), NULL);
2689 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2690 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2691 gtk_widget_show(g->inspector_window);
2693 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2694 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2695 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2696 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2697 gtk_widget_show(scrolled_window);
2699 new_web_view = webkit_web_view_new();
2700 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2702 return WEBKIT_WEB_VIEW(new_web_view);
2706 inspector_show_window_cb (WebKitWebInspector* inspector){
2708 gtk_widget_show(uzbl.gui.inspector_window);
2712 /* TODO: Add variables and code to make use of these functions */
2714 inspector_close_window_cb (WebKitWebInspector* inspector){
2720 inspector_attach_window_cb (WebKitWebInspector* inspector){
2726 inspector_detach_window_cb (WebKitWebInspector* inspector){
2732 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2738 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2744 set_up_inspector() {
2746 WebKitWebSettings *settings = view_settings();
2747 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2749 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2750 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2751 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2752 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2753 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2754 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2755 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2757 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2761 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2763 uzbl_cmdprop *c = v;
2768 if(c->type == TYPE_STR)
2769 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2770 else if(c->type == TYPE_INT)
2771 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2772 else if(c->type == TYPE_FLOAT)
2773 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2777 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2781 printf("bind %s = %s %s\n", (char *)k ,
2782 (char *)a->name, a->param?(char *)a->param:"");
2787 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2788 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2792 retrieve_geometry() {
2794 GString *buf = g_string_new("");
2796 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2797 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2799 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2801 if(uzbl.gui.geometry)
2802 g_free(uzbl.gui.geometry);
2803 uzbl.gui.geometry = g_string_free(buf, FALSE);
2806 /* set up gtk, gobject, variable defaults and other things that tests and other
2807 * external applications need to do anyhow */
2809 initialize(int argc, char *argv[]) {
2810 if (!g_thread_supported ())
2811 g_thread_init (NULL);
2812 uzbl.state.executable_path = g_strdup(argv[0]);
2813 uzbl.state.selected_url = NULL;
2814 uzbl.state.searchtx = NULL;
2816 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2817 g_option_context_add_main_entries (context, entries, NULL);
2818 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2819 g_option_context_parse (context, &argc, &argv, NULL);
2820 g_option_context_free(context);
2822 if (uzbl.behave.print_version) {
2823 printf("Commit: %s\n", COMMIT);
2827 /* initialize hash table */
2828 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2830 uzbl.net.soup_session = webkit_get_default_session();
2831 uzbl.state.keycmd = g_strdup("");
2833 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2834 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2835 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2836 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2837 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2838 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2840 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2841 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2842 uzbl.gui.sbar.progress_w = 10;
2844 /* HTML mode defaults*/
2845 uzbl.behave.html_buffer = g_string_new("");
2846 uzbl.behave.html_endmarker = g_strdup(".");
2847 uzbl.behave.html_timeout = 60;
2848 uzbl.behave.base_url = g_strdup("http://invalid");
2850 /* default mode indicators */
2851 uzbl.behave.insert_indicator = g_strdup("I");
2852 uzbl.behave.cmd_indicator = g_strdup("C");
2854 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2855 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2856 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2857 uzbl.info.arch = ARCH;
2858 uzbl.info.commit = COMMIT;
2861 make_var_to_name_hash();
2866 #ifndef UZBL_LIBRARY
2869 main (int argc, char* argv[]) {
2870 initialize(argc, argv);
2872 gtk_init (&argc, &argv);
2874 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2875 //main_window_ref = g_object_ref(scrolled_window);
2876 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2877 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2879 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2880 GTK_WIDGET (uzbl.gui.web_view));
2882 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2886 /* initial packing */
2887 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2888 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2890 if (uzbl.state.socket_id) {
2891 uzbl.gui.plug = create_plug ();
2892 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2893 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2895 uzbl.gui.main_window = create_window ();
2896 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2897 gtk_widget_show_all (uzbl.gui.main_window);
2898 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2901 if(!uzbl.state.instance_name)
2902 uzbl.state.instance_name = itos((int)uzbl.xwin);
2904 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2906 if (uzbl.state.verbose) {
2907 printf("Uzbl start location: %s\n", argv[0]);
2908 if (uzbl.state.socket_id)
2909 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2911 printf("window_id %i\n",(int) uzbl.xwin);
2912 printf("pid %i\n", getpid ());
2913 printf("name: %s\n", uzbl.state.instance_name);
2916 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2917 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2918 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2919 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2920 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2922 if(uzbl.gui.geometry)
2925 retrieve_geometry();
2927 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2928 if (argc > 1 && !uzbl.state.uri)
2929 uri_override = g_strdup(argv[1]);
2930 gboolean verbose_override = uzbl.state.verbose;
2933 set_insert_mode(FALSE);
2935 if (!uzbl.behave.show_status)
2936 gtk_widget_hide(uzbl.gui.mainbar);
2943 if (verbose_override > uzbl.state.verbose)
2944 uzbl.state.verbose = verbose_override;
2947 set_var_value("uri", uri_override);
2948 g_free(uri_override);
2949 } else if (uzbl.state.uri)
2955 return EXIT_SUCCESS;
2959 /* vi: set et ts=4: */