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])
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
45 #include <webkit/webkit.h>
46 #include <libsoup/soup.h>
47 #include <JavaScriptCore/JavaScript.h>
58 #include <sys/ioctl.h>
65 /* commandline arguments (set initial values for the state variables) */
67 GOptionEntry entries[] =
69 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
70 "Uri to load at startup (equivalent to 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "URI" },
71 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
72 "Whether to print all messages or just errors.", NULL },
73 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
74 "Name of the current instance (defaults to Xorg window id)", "NAME" },
75 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
76 "Path to config file or '-' for stdin", "FILE" },
77 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
78 "Socket ID", "SOCKET" },
79 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
80 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
81 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
82 "Print the version and exit", NULL },
83 { NULL, 0, 0, 0, NULL, NULL, NULL }
86 enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
88 /* associate command names to their properties */
89 typedef const struct {
90 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
91 the PTR() macro is kind of preventing this change at the moment. */
100 /*@null@*/ void (*func)(void);
103 /* abbreviations to help keep the table's width humane */
104 #define PTR_V_STR(var, d, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = d, .writeable = 1, .func = fun }
105 #define PTR_V_INT(var, d, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = d, .writeable = 1, .func = fun }
106 #define PTR_V_FLOAT(var, d, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = d, .writeable = 1, .func = fun }
107 #define PTR_C_STR(var, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = 0, .writeable = 0, .func = fun }
108 #define PTR_C_INT(var, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = 0, .writeable = 0, .func = fun }
109 #define PTR_C_FLOAT(var, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = 0, .writeable = 0, .func = fun }
111 const struct var_name_to_ptr_t {
114 } var_name_to_ptr[] = {
115 /* variable name pointer to variable in code dump callback function */
116 /* ---------------------------------------------------------------------------------------------- */
117 { "uri", PTR_V_STR(uzbl.state.uri, 1, cmd_load_uri)},
118 { "verbose", PTR_V_INT(uzbl.state.verbose, 1, NULL)},
119 { "mode", PTR_V_INT(uzbl.behave.mode, 0, NULL)},
120 { "inject_html", PTR_V_STR(uzbl.behave.inject_html, 0, cmd_inject_html)},
121 { "base_url", PTR_V_STR(uzbl.behave.base_url, 1, NULL)},
122 { "html_endmarker", PTR_V_STR(uzbl.behave.html_endmarker, 1, NULL)},
123 { "html_mode_timeout", PTR_V_INT(uzbl.behave.html_timeout, 1, NULL)},
124 { "keycmd", PTR_V_STR(uzbl.state.keycmd, 1, set_keycmd)},
125 { "status_message", PTR_V_STR(uzbl.gui.sbar.msg, 1, update_title)},
126 { "show_status", PTR_V_INT(uzbl.behave.show_status, 1, cmd_set_status)},
127 { "status_top", PTR_V_INT(uzbl.behave.status_top, 1, move_statusbar)},
128 { "status_format", PTR_V_STR(uzbl.behave.status_format, 1, update_title)},
129 { "status_pbar_done", PTR_V_STR(uzbl.gui.sbar.progress_s, 1, update_title)},
130 { "status_pbar_pending", PTR_V_STR(uzbl.gui.sbar.progress_u, 1, update_title)},
131 { "status_pbar_width", PTR_V_INT(uzbl.gui.sbar.progress_w, 1, update_title)},
132 { "status_background", PTR_V_STR(uzbl.behave.status_background, 1, update_title)},
133 { "insert_indicator", PTR_V_STR(uzbl.behave.insert_indicator, 1, update_indicator)},
134 { "command_indicator", PTR_V_STR(uzbl.behave.cmd_indicator, 1, update_indicator)},
135 { "title_format_long", PTR_V_STR(uzbl.behave.title_format_long, 1, update_title)},
136 { "title_format_short", PTR_V_STR(uzbl.behave.title_format_short, 1, update_title)},
137 { "icon", PTR_V_STR(uzbl.gui.icon, 1, set_icon)},
138 { "insert_mode", PTR_V_INT(uzbl.behave.insert_mode, 1, set_mode_indicator)},
139 { "always_insert_mode", PTR_V_INT(uzbl.behave.always_insert_mode, 1, cmd_always_insert_mode)},
140 { "reset_command_mode", PTR_V_INT(uzbl.behave.reset_command_mode, 1, NULL)},
141 { "modkey", PTR_V_STR(uzbl.behave.modkey, 1, cmd_modkey)},
142 { "load_finish_handler", PTR_V_STR(uzbl.behave.load_finish_handler, 1, NULL)},
143 { "load_start_handler", PTR_V_STR(uzbl.behave.load_start_handler, 1, NULL)},
144 { "load_commit_handler", PTR_V_STR(uzbl.behave.load_commit_handler, 1, NULL)},
145 { "history_handler", PTR_V_STR(uzbl.behave.history_handler, 1, NULL)},
146 { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)},
147 { "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, cmd_cookie_handler)},
148 { "new_window", PTR_V_STR(uzbl.behave.new_window, 1, cmd_new_window)},
149 { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)},
150 { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)},
151 { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)},
152 { "shell_cmd", PTR_V_STR(uzbl.behave.shell_cmd, 1, NULL)},
153 { "proxy_url", PTR_V_STR(uzbl.net.proxy_url, 1, set_proxy_url)},
154 { "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, cmd_max_conns)},
155 { "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, cmd_max_conns_host)},
156 { "useragent", PTR_V_STR(uzbl.net.useragent, 1, cmd_useragent)},
158 /* exported WebKitWebSettings properties */
159 { "zoom_level", PTR_V_FLOAT(uzbl.behave.zoom_level, 1, cmd_zoom_level)},
160 { "font_size", PTR_V_INT(uzbl.behave.font_size, 1, cmd_font_size)},
161 { "default_font_family", PTR_V_STR(uzbl.behave.default_font_family, 1, cmd_default_font_family)},
162 { "monospace_font_family", PTR_V_STR(uzbl.behave.monospace_font_family, 1, cmd_monospace_font_family)},
163 { "cursive_font_family", PTR_V_STR(uzbl.behave.cursive_font_family, 1, cmd_cursive_font_family)},
164 { "sans_serif_font_family", PTR_V_STR(uzbl.behave.sans_serif_font_family, 1, cmd_sans_serif_font_family)},
165 { "serif_font_family", PTR_V_STR(uzbl.behave.serif_font_family, 1, cmd_serif_font_family)},
166 { "fantasy_font_family", PTR_V_STR(uzbl.behave.fantasy_font_family, 1, cmd_fantasy_font_family)},
167 { "monospace_size", PTR_V_INT(uzbl.behave.monospace_size, 1, cmd_font_size)},
168 { "minimum_font_size", PTR_V_INT(uzbl.behave.minimum_font_size, 1, cmd_minimum_font_size)},
169 { "disable_plugins", PTR_V_INT(uzbl.behave.disable_plugins, 1, cmd_disable_plugins)},
170 { "disable_scripts", PTR_V_INT(uzbl.behave.disable_scripts, 1, cmd_disable_scripts)},
171 { "autoload_images", PTR_V_INT(uzbl.behave.autoload_img, 1, cmd_autoload_img)},
172 { "autoshrink_images", PTR_V_INT(uzbl.behave.autoshrink_img, 1, cmd_autoshrink_img)},
173 { "enable_spellcheck", PTR_V_INT(uzbl.behave.enable_spellcheck, 1, cmd_enable_spellcheck)},
174 { "enable_private", PTR_V_INT(uzbl.behave.enable_private, 1, cmd_enable_private)},
175 { "print_backgrounds", PTR_V_INT(uzbl.behave.print_bg, 1, cmd_print_bg)},
176 { "stylesheet_uri", PTR_V_STR(uzbl.behave.style_uri, 1, cmd_style_uri)},
177 { "resizable_text_areas", PTR_V_INT(uzbl.behave.resizable_txt, 1, cmd_resizable_txt)},
178 { "default_encoding", PTR_V_STR(uzbl.behave.default_encoding, 1, cmd_default_encoding)},
179 { "enforce_96_dpi", PTR_V_INT(uzbl.behave.enforce_96dpi, 1, cmd_enforce_96dpi)},
180 { "caret_browsing", PTR_V_INT(uzbl.behave.caret_browsing, 1, cmd_caret_browsing)},
182 /* constants (not dumpable or writeable) */
183 { "WEBKIT_MAJOR", PTR_C_INT(uzbl.info.webkit_major, NULL)},
184 { "WEBKIT_MINOR", PTR_C_INT(uzbl.info.webkit_minor, NULL)},
185 { "WEBKIT_MICRO", PTR_C_INT(uzbl.info.webkit_micro, NULL)},
186 { "ARCH_UZBL", PTR_C_STR(uzbl.info.arch, NULL)},
187 { "COMMIT", PTR_C_STR(uzbl.info.commit, NULL)},
188 { "LOAD_PROGRESS", PTR_C_INT(uzbl.gui.sbar.load_progress, NULL)},
189 { "LOAD_PROGRESSBAR", PTR_C_STR(uzbl.gui.sbar.progress_bar, NULL)},
190 { "TITLE", PTR_C_STR(uzbl.gui.main_title, NULL)},
191 { "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url, NULL)},
192 { "MODE", PTR_C_STR(uzbl.gui.sbar.mode_indicator, NULL)},
193 { "NAME", PTR_C_STR(uzbl.state.instance_name, NULL)},
195 { NULL, {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
200 /*@null@*/ char *key;
203 { "SHIFT", GDK_SHIFT_MASK }, // shift
204 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
205 { "CONTROL", GDK_CONTROL_MASK }, // control
206 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
207 { "MOD2", GDK_MOD2_MASK }, // 5th mod
208 { "MOD3", GDK_MOD3_MASK }, // 6th mod
209 { "MOD4", GDK_MOD4_MASK }, // 7th mod
210 { "MOD5", GDK_MOD5_MASK }, // 8th mod
211 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
212 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
213 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
214 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
215 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
216 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
217 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
218 { "META", GDK_META_MASK }, // meta (since 2.10)
223 /* construct a hash from the var_name_to_ptr array for quick access */
225 make_var_to_name_hash() {
226 const struct var_name_to_ptr_t *n2v_p = var_name_to_ptr;
227 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
229 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
234 /* --- UTILITY FUNCTIONS --- */
235 enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
237 get_exp_type(const gchar *s) {
241 else if(*(s+1) == '{')
242 return EXP_BRACED_VAR;
243 else if(*(s+1) == '<')
245 else if(*(s+1) == '[')
248 return EXP_SIMPLE_VAR;
255 * recurse == 1: don't expand '@(command)@'
256 * recurse == 2: don't expand '@<java script>@'
259 expand(const char *s, guint recurse) {
262 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
266 gchar *cmd_stdout = NULL;
268 GString *buf = g_string_new("");
269 GString *js_ret = g_string_new("");
274 g_string_append_c(buf, *++s);
279 etype = get_exp_type(s);
284 vend = strpbrk(s, end_simple_var);
285 if(!vend) vend = strchr(s, '\0');
289 vend = strchr(s, '}');
290 if(!vend) vend = strchr(s, '\0');
294 vend = strstr(s, ")@");
295 if(!vend) vend = strchr(s, '\0');
299 vend = strstr(s, ">@");
300 if(!vend) vend = strchr(s, '\0');
304 vend = strstr(s, "]@");
305 if(!vend) vend = strchr(s, '\0');
310 ret = g_strndup(s, vend-s);
312 if(etype == EXP_SIMPLE_VAR ||
313 etype == EXP_BRACED_VAR) {
314 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
315 if(c->type == TYPE_STR && *c->ptr.s != NULL) {
316 g_string_append(buf, (gchar *)*c->ptr.s);
317 } else if(c->type == TYPE_INT) {
318 g_string_append_printf(buf, "%d", *c->ptr.i);
320 else if(c->type == TYPE_FLOAT) {
321 g_string_append_printf(buf, "%f", *c->ptr.f);
325 if(etype == EXP_SIMPLE_VAR)
330 else if(recurse != 1 &&
332 mycmd = expand(ret, 1);
333 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
337 g_printerr("error on running command: %s\n", err->message);
340 else if (*cmd_stdout) {
341 size_t len = strlen(cmd_stdout);
343 if(len > 0 && cmd_stdout[len-1] == '\n')
344 cmd_stdout[--len] = '\0'; /* strip trailing newline */
346 g_string_append(buf, cmd_stdout);
351 else if(recurse != 2 &&
353 mycmd = expand(ret, 2);
354 eval_js(uzbl.gui.web_view, mycmd, js_ret);
358 g_string_append(buf, js_ret->str);
359 g_string_free(js_ret, TRUE);
360 js_ret = g_string_new("");
364 else if(etype == EXP_ESCAPE) {
365 mycmd = expand(ret, 0);
366 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
368 g_string_append(buf, escaped);
380 g_string_append_c(buf, *s);
385 g_string_free(js_ret, TRUE);
386 return g_string_free(buf, FALSE);
393 snprintf(tmp, sizeof(tmp), "%i", val);
394 return g_strdup(tmp);
398 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
401 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
404 str_replace (const char* search, const char* replace, const char* string) {
408 buf = g_strsplit (string, search, -1);
409 ret = g_strjoinv (replace, buf);
410 g_strfreev(buf); // somebody said this segfaults
416 read_file_by_line (const gchar *path) {
417 GIOChannel *chan = NULL;
418 gchar *readbuf = NULL;
420 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
423 chan = g_io_channel_new_file(path, "r", NULL);
426 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
427 const gchar* val = g_strdup (readbuf);
428 g_array_append_val (lines, val);
433 g_io_channel_unref (chan);
435 fprintf(stderr, "File '%s' not be read.\n", path);
442 parseenv (char* string) {
443 extern char** environ;
444 gchar* tmpstr = NULL;
448 while (environ[i] != NULL) {
449 gchar** env = g_strsplit (environ[i], "=", 2);
450 gchar* envname = g_strconcat ("$", env[0], NULL);
452 if (g_strrstr (string, envname) != NULL) {
453 tmpstr = g_strdup(string);
455 string = str_replace(envname, env[1], tmpstr);
460 g_strfreev (env); // somebody said this breaks uzbl
468 setup_signal(int signr, sigfunc *shandler) {
469 struct sigaction nh, oh;
471 nh.sa_handler = shandler;
472 sigemptyset(&nh.sa_mask);
475 if(sigaction(signr, &nh, &oh) < 0)
483 if (uzbl.behave.fifo_dir)
484 unlink (uzbl.comm.fifo_path);
485 if (uzbl.behave.socket_dir)
486 unlink (uzbl.comm.socket_path);
488 g_free(uzbl.state.executable_path);
489 g_free(uzbl.state.keycmd);
490 g_hash_table_destroy(uzbl.bindings);
491 g_hash_table_destroy(uzbl.behave.commands);
494 /* used for html_mode_timeout
495 * be sure to extend this function to use
496 * more timers if needed in other places
499 set_timeout(int seconds) {
501 memset(&t, 0, sizeof t);
503 t.it_value.tv_sec = seconds;
504 t.it_value.tv_usec = 0;
505 setitimer(ITIMER_REAL, &t, NULL);
508 /* --- SIGNAL HANDLER --- */
511 catch_sigterm(int s) {
517 catch_sigint(int s) {
527 set_var_value("mode", "0");
532 /* --- CALLBACKS --- */
535 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
538 (void) navigation_action;
539 (void) policy_decision;
541 const gchar* uri = webkit_network_request_get_uri (request);
542 if (uzbl.state.verbose)
543 printf("New window requested -> %s \n", uri);
544 webkit_web_policy_decision_use(policy_decision);
549 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
554 /* If we can display it, let's display it... */
555 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
556 webkit_web_policy_decision_use (policy_decision);
560 /* ...everything we can't displayed is downloaded */
561 webkit_web_policy_decision_download (policy_decision);
565 /*@null@*/ WebKitWebView*
566 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
570 if (uzbl.state.selected_url != NULL) {
571 if (uzbl.state.verbose)
572 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
573 new_window_load_uri(uzbl.state.selected_url);
575 if (uzbl.state.verbose)
576 printf("New web view -> %s\n","Nothing to open, exiting");
582 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
585 if (uzbl.behave.download_handler) {
586 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
587 if (uzbl.state.verbose)
588 printf("Download -> %s\n",uri);
589 /* if urls not escaped, we may have to escape and quote uri before this call */
590 run_handler(uzbl.behave.download_handler, uri);
595 /* scroll a bar in a given direction */
597 scroll (GtkAdjustment* bar, GArray *argv) {
601 gdouble page_size = gtk_adjustment_get_page_size(bar);
602 gdouble value = gtk_adjustment_get_value(bar);
603 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
606 value += page_size * amount * 0.01;
610 max_value = gtk_adjustment_get_upper(bar) - page_size;
612 if (value > max_value)
613 value = max_value; /* don't scroll past the end of the page */
615 gtk_adjustment_set_value (bar, value);
619 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
620 (void) page; (void) argv; (void) result;
621 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
625 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
626 (void) page; (void) argv; (void) result;
627 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
628 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
632 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
633 (void) page; (void) result;
634 scroll(uzbl.gui.bar_v, argv);
638 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
639 (void) page; (void) result;
640 scroll(uzbl.gui.bar_h, argv);
645 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
646 if(uzbl.state.verbose)
647 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
649 /* update geometry var with the actual geometry
650 this is necessary as some WMs don't seem to honour
651 the above setting and we don't want to end up with
652 wrong geometry information
659 if (!uzbl.behave.show_status) {
660 gtk_widget_hide(uzbl.gui.mainbar);
662 gtk_widget_show(uzbl.gui.mainbar);
668 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
673 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
677 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
682 if (uzbl.behave.show_status) {
683 gtk_widget_hide(uzbl.gui.mainbar);
685 gtk_widget_show(uzbl.gui.mainbar);
687 uzbl.behave.show_status = !uzbl.behave.show_status;
692 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
696 //Set selected_url state variable
697 g_free(uzbl.state.selected_url);
698 uzbl.state.selected_url = NULL;
700 uzbl.state.selected_url = g_strdup(link);
706 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
709 const gchar *title = webkit_web_view_get_title(web_view);
710 if (uzbl.gui.main_title)
711 g_free (uzbl.gui.main_title);
712 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
717 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
720 uzbl.gui.sbar.load_progress = progress;
722 g_free(uzbl.gui.sbar.progress_bar);
723 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
729 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
733 if (uzbl.behave.load_finish_handler)
734 run_handler(uzbl.behave.load_finish_handler, "");
737 void clear_keycmd() {
738 g_free(uzbl.state.keycmd);
739 uzbl.state.keycmd = g_strdup("");
743 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
747 uzbl.gui.sbar.load_progress = 0;
748 clear_keycmd(); // don't need old commands to remain on new page?
749 if (uzbl.behave.load_start_handler)
750 run_handler(uzbl.behave.load_start_handler, "");
754 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
757 g_free (uzbl.state.uri);
758 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
759 uzbl.state.uri = g_string_free (newuri, FALSE);
760 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
761 set_insert_mode(uzbl.behave.always_insert_mode);
764 if (uzbl.behave.load_commit_handler)
765 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
769 destroy_cb (GtkWidget* widget, gpointer data) {
777 if (uzbl.behave.history_handler) {
779 struct tm * timeinfo;
782 timeinfo = localtime ( &rawtime );
783 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
784 run_handler(uzbl.behave.history_handler, date);
789 /* VIEW funcs (little webkit wrappers) */
790 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
792 VIEWFUNC(reload_bypass_cache)
793 VIEWFUNC(stop_loading)
800 /* -- command to callback/function map for things we cannot attach to any signals */
801 struct {const char *key; CommandInfo value;} cmdlist[] =
802 { /* key function no_split */
803 { "back", {view_go_back, 0} },
804 { "forward", {view_go_forward, 0} },
805 { "scroll_vert", {scroll_vert, 0} },
806 { "scroll_horz", {scroll_horz, 0} },
807 { "scroll_begin", {scroll_begin, 0} },
808 { "scroll_end", {scroll_end, 0} },
809 { "reload", {view_reload, 0}, },
810 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
811 { "stop", {view_stop_loading, 0}, },
812 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
813 { "zoom_out", {view_zoom_out, 0}, },
814 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
815 { "uri", {load_uri, TRUE} },
816 { "js", {run_js, TRUE} },
817 { "script", {run_external_js, 0} },
818 { "toggle_status", {toggle_status_cb, 0} },
819 { "spawn", {spawn, 0} },
820 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
821 { "sh", {spawn_sh, 0} },
822 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
823 { "talk_to_socket", {talk_to_socket, 0} },
824 { "exit", {close_uzbl, 0} },
825 { "search", {search_forward_text, TRUE} },
826 { "search_reverse", {search_reverse_text, TRUE} },
827 { "dehilight", {dehilight, 0} },
828 { "toggle_insert_mode", {toggle_insert_mode, 0} },
829 { "set", {set_var, TRUE} },
830 //{ "get", {get_var, TRUE} },
831 { "bind", {act_bind, TRUE} },
832 { "dump_config", {act_dump_config, 0} },
833 { "keycmd", {keycmd, TRUE} },
834 { "keycmd_nl", {keycmd_nl, TRUE} },
835 { "keycmd_bs", {keycmd_bs, 0} },
836 { "chain", {chain, 0} },
837 { "print", {print, TRUE} }
844 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
846 for (i = 0; i < LENGTH(cmdlist); i++)
847 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
850 /* -- CORE FUNCTIONS -- */
853 free_action(gpointer act) {
854 Action *action = (Action*)act;
855 g_free(action->name);
857 g_free(action->param);
862 new_action(const gchar *name, const gchar *param) {
863 Action *action = g_new(Action, 1);
865 action->name = g_strdup(name);
867 action->param = g_strdup(param);
869 action->param = NULL;
875 file_exists (const char * filename) {
876 return (access(filename, F_OK) == 0);
880 set_var(WebKitWebView *page, GArray *argv, GString *result) {
881 (void) page; (void) result;
882 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
883 if (split[0] != NULL) {
884 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
885 set_var_value(g_strstrip(split[0]), value);
892 print(WebKitWebView *page, GArray *argv, GString *result) {
893 (void) page; (void) result;
896 buf = expand(argv_idx(argv, 0), 0);
897 g_string_assign(result, buf);
902 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
903 (void) page; (void) result;
904 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
905 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
906 add_binding(g_strstrip(split[0]), value);
924 set_mode_indicator() {
925 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
926 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
931 set_mode_indicator();
936 set_insert_mode(gboolean mode) {
937 uzbl.behave.insert_mode = mode;
938 set_mode_indicator();
942 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
943 (void) page; (void) result;
945 if (argv_idx(argv, 0)) {
946 if (strcmp (argv_idx(argv, 0), "0") == 0) {
947 set_insert_mode(FALSE);
949 set_insert_mode(TRUE);
952 set_insert_mode( !uzbl.behave.insert_mode );
959 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
962 if (argv_idx(argv, 0)) {
963 GString* newuri = g_string_new (argv_idx(argv, 0));
964 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
965 run_js(web_view, argv, NULL);
968 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
969 g_string_prepend (newuri, "http://");
970 /* if we do handle cookies, ask our handler for them */
971 webkit_web_view_load_uri (web_view, newuri->str);
972 g_string_free (newuri, TRUE);
979 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
980 size_t argumentCount, const JSValueRef arguments[],
981 JSValueRef* exception) {
986 JSStringRef js_result_string;
987 GString *result = g_string_new("");
989 if (argumentCount >= 1) {
990 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
991 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
992 char ctl_line[arg_size];
993 JSStringGetUTF8CString(arg, ctl_line, arg_size);
995 parse_cmd_line(ctl_line, result);
997 JSStringRelease(arg);
999 js_result_string = JSStringCreateWithUTF8CString(result->str);
1001 g_string_free(result, TRUE);
1003 return JSValueMakeString(ctx, js_result_string);
1006 JSStaticFunction js_static_functions[] = {
1007 {"run", js_run_command, kJSPropertyAttributeNone},
1012 /* This function creates the class and its definition, only once */
1013 if (!uzbl.js.initialized) {
1014 /* it would be pretty cool to make this dynamic */
1015 uzbl.js.classdef = kJSClassDefinitionEmpty;
1016 uzbl.js.classdef.staticFunctions = js_static_functions;
1018 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1024 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1025 WebKitWebFrame *frame;
1026 JSGlobalContextRef context;
1027 JSObjectRef globalobject;
1028 JSStringRef var_name;
1030 JSStringRef js_script;
1031 JSValueRef js_result;
1032 JSStringRef js_result_string;
1033 size_t js_result_size;
1037 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1038 context = webkit_web_frame_get_global_context(frame);
1039 globalobject = JSContextGetGlobalObject(context);
1041 /* uzbl javascript namespace */
1042 var_name = JSStringCreateWithUTF8CString("Uzbl");
1043 JSObjectSetProperty(context, globalobject, var_name,
1044 JSObjectMake(context, uzbl.js.classref, NULL),
1045 kJSClassAttributeNone, NULL);
1047 /* evaluate the script and get return value*/
1048 js_script = JSStringCreateWithUTF8CString(script);
1049 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1050 if (js_result && !JSValueIsUndefined(context, js_result)) {
1051 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1052 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1054 if (js_result_size) {
1055 char js_result_utf8[js_result_size];
1056 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1057 g_string_assign(result, js_result_utf8);
1060 JSStringRelease(js_result_string);
1064 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1066 JSStringRelease(var_name);
1067 JSStringRelease(js_script);
1071 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1072 if (argv_idx(argv, 0))
1073 eval_js(web_view, argv_idx(argv, 0), result);
1077 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1079 if (argv_idx(argv, 0)) {
1080 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1085 while ((line = g_array_index(lines, gchar*, i))) {
1087 js = g_strdup (line);
1089 gchar* newjs = g_strconcat (js, line, NULL);
1096 if (uzbl.state.verbose)
1097 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1099 if (argv_idx (argv, 1)) {
1100 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1104 eval_js (web_view, js, result);
1106 g_array_free (lines, TRUE);
1111 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1112 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1113 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1114 webkit_web_view_unmark_text_matches (page);
1115 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1116 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1120 if (uzbl.state.searchtx) {
1121 if (uzbl.state.verbose)
1122 printf ("Searching: %s\n", uzbl.state.searchtx);
1123 webkit_web_view_set_highlight_text_matches (page, TRUE);
1124 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1129 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1131 search_text(page, argv, TRUE);
1135 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1137 search_text(page, argv, FALSE);
1141 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1142 (void) argv; (void) result;
1143 webkit_web_view_set_highlight_text_matches (page, FALSE);
1148 new_window_load_uri (const gchar * uri) {
1149 if (uzbl.behave.new_window) {
1150 GString *s = g_string_new ("");
1151 g_string_printf(s, "'%s'", uri);
1152 run_handler(uzbl.behave.new_window, s->str);
1155 GString* to_execute = g_string_new ("");
1156 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1158 for (i = 0; entries[i].long_name != NULL; i++) {
1159 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1160 gchar** str = (gchar**)entries[i].arg_data;
1162 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1166 if (uzbl.state.verbose)
1167 printf("\n%s\n", to_execute->str);
1168 g_spawn_command_line_async (to_execute->str, NULL);
1169 g_string_free (to_execute, TRUE);
1173 chain (WebKitWebView *page, GArray *argv, GString *result) {
1174 (void) page; (void) result;
1176 gchar **parts = NULL;
1178 while ((a = argv_idx(argv, i++))) {
1179 parts = g_strsplit (a, " ", 2);
1181 parse_command(parts[0], parts[1], result);
1187 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1191 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1197 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1201 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1207 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1212 int len = strlen(uzbl.state.keycmd);
1213 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1215 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1220 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1227 /* --Statusbar functions-- */
1229 build_progressbar_ascii(int percent) {
1230 int width=uzbl.gui.sbar.progress_w;
1233 GString *bar = g_string_new("");
1235 l = (double)percent*((double)width/100.);
1236 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1238 for(i=0; i<(int)l; i++)
1239 g_string_append(bar, uzbl.gui.sbar.progress_s);
1242 g_string_append(bar, uzbl.gui.sbar.progress_u);
1244 return g_string_free(bar, FALSE);
1246 /* --End Statusbar functions-- */
1249 sharg_append(GArray *a, const gchar *str) {
1250 const gchar *s = (str ? str : "");
1251 g_array_append_val(a, s);
1254 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1256 run_command (const gchar *command, const guint npre, const gchar **args,
1257 const gboolean sync, char **output_stdout) {
1258 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1261 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1262 gchar *pid = itos(getpid());
1263 gchar *xwin = itos(uzbl.xwin);
1265 sharg_append(a, command);
1266 for (i = 0; i < npre; i++) /* add n args before the default vars */
1267 sharg_append(a, args[i]);
1268 sharg_append(a, uzbl.state.config_file);
1269 sharg_append(a, pid);
1270 sharg_append(a, xwin);
1271 sharg_append(a, uzbl.comm.fifo_path);
1272 sharg_append(a, uzbl.comm.socket_path);
1273 sharg_append(a, uzbl.state.uri);
1274 sharg_append(a, uzbl.gui.main_title);
1276 for (i = npre; i < g_strv_length((gchar**)args); i++)
1277 sharg_append(a, args[i]);
1281 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1283 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1284 NULL, NULL, output_stdout, NULL, NULL, &err);
1285 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1286 NULL, NULL, NULL, &err);
1288 if (uzbl.state.verbose) {
1289 GString *s = g_string_new("spawned:");
1290 for (i = 0; i < (a->len); i++) {
1291 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1292 g_string_append_printf(s, " %s", qarg);
1295 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1296 printf("%s\n", s->str);
1297 g_string_free(s, TRUE);
1299 printf("Stdout: %s\n", *output_stdout);
1303 g_printerr("error on run_command: %s\n", err->message);
1308 g_array_free (a, TRUE);
1313 split_quoted(const gchar* src, const gboolean unquote) {
1314 /* split on unquoted space, return array of strings;
1315 remove a layer of quotes and backslashes if unquote */
1316 if (!src) return NULL;
1318 gboolean dq = FALSE;
1319 gboolean sq = FALSE;
1320 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1321 GString *s = g_string_new ("");
1325 for (p = src; *p != '\0'; p++) {
1326 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1327 else if (*p == '\\') { g_string_append_c(s, *p++);
1328 g_string_append_c(s, *p); }
1329 else if ((*p == '"') && unquote && !sq) dq = !dq;
1330 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1332 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1333 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1335 else if ((*p == ' ') && !dq && !sq) {
1336 dup = g_strdup(s->str);
1337 g_array_append_val(a, dup);
1338 g_string_truncate(s, 0);
1339 } else g_string_append_c(s, *p);
1341 dup = g_strdup(s->str);
1342 g_array_append_val(a, dup);
1343 ret = (gchar**)a->data;
1344 g_array_free (a, FALSE);
1345 g_string_free (s, TRUE);
1350 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1351 (void)web_view; (void)result;
1352 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1353 if (argv_idx(argv, 0))
1354 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1358 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1359 (void)web_view; (void)result;
1361 if (argv_idx(argv, 0))
1362 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1363 TRUE, &uzbl.comm.sync_stdout);
1367 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1368 (void)web_view; (void)result;
1369 if (!uzbl.behave.shell_cmd) {
1370 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1375 gchar *spacer = g_strdup("");
1376 g_array_insert_val(argv, 1, spacer);
1377 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1379 for (i = 1; i < g_strv_length(cmd); i++)
1380 g_array_prepend_val(argv, cmd[i]);
1382 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1388 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1389 (void)web_view; (void)result;
1390 if (!uzbl.behave.shell_cmd) {
1391 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1396 gchar *spacer = g_strdup("");
1397 g_array_insert_val(argv, 1, spacer);
1398 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1400 for (i = 1; i < g_strv_length(cmd); i++)
1401 g_array_prepend_val(argv, cmd[i]);
1403 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1404 TRUE, &uzbl.comm.sync_stdout);
1410 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1411 (void)web_view; (void)result;
1414 struct sockaddr_un sa;
1421 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1423 /* This function could be optimised by storing a hash table of socket paths
1424 and associated connected file descriptors rather than closing and
1425 re-opening for every call. Also we could launch a script if socket connect
1428 /* First element argv[0] is path to socket. Following elements are tokens to
1429 write to the socket. We write them as a single packet with each token
1430 separated by an ASCII nul (\0). */
1432 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1437 /* copy socket path, null terminate result */
1438 sockpath = g_array_index(argv, char*, 0);
1439 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1440 sa.sun_family = AF_UNIX;
1442 /* create socket file descriptor and connect it to path */
1443 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1445 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1448 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1449 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1454 /* build request vector */
1455 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1457 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1461 for(i = 1; i < argv->len; ++i) {
1462 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1463 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1467 ret = writev(fd, iov, argv->len - 1);
1470 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1475 /* wait for a response, with a 500ms timeout */
1477 pfd.events = POLLIN;
1479 ret = poll(&pfd, 1, 500);
1481 if(ret == 0) errno = ETIMEDOUT;
1482 if(errno == EINTR) continue;
1483 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1489 /* get length of response */
1490 if(ioctl(fd, FIONREAD, &len) == -1) {
1491 g_printerr("talk_to_socket: cannot find daemon response length, "
1492 "ioctl failed (%s)\n", strerror(errno));
1497 /* if there is a response, read it */
1499 uzbl.comm.sync_stdout = g_malloc(len + 1);
1500 if(!uzbl.comm.sync_stdout) {
1501 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1505 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1507 ret = read(fd, uzbl.comm.sync_stdout, len);
1509 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1522 parse_command(const char *cmd, const char *param, GString *result) {
1525 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1527 gchar **par = split_quoted(param, TRUE);
1528 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1530 if (c->no_split) { /* don't split */
1531 sharg_append(a, param);
1533 for (i = 0; i < g_strv_length(par); i++)
1534 sharg_append(a, par[i]);
1537 if (result == NULL) {
1538 GString *result_print = g_string_new("");
1540 c->function(uzbl.gui.web_view, a, result_print);
1541 if (result_print->len)
1542 printf("%*s\n", (int)result_print->len, result_print->str);
1544 g_string_free(result_print, TRUE);
1546 c->function(uzbl.gui.web_view, a, result);
1549 g_array_free (a, TRUE);
1552 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1559 if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
1560 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1561 (GType) SOUP_SESSION_PROXY_URI);
1564 suri = soup_uri_new(uzbl.net.proxy_url);
1565 g_object_set(G_OBJECT(uzbl.net.soup_session),
1566 SOUP_SESSION_PROXY_URI,
1568 soup_uri_free(suri);
1575 if(file_exists(uzbl.gui.icon)) {
1576 if (uzbl.gui.main_window)
1577 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1579 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1585 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1586 g_array_append_val (a, uzbl.state.uri);
1587 load_uri(uzbl.gui.web_view, a, NULL);
1588 g_array_free (a, TRUE);
1592 cmd_always_insert_mode() {
1593 set_insert_mode(uzbl.behave.always_insert_mode);
1599 g_object_set(G_OBJECT(uzbl.net.soup_session),
1600 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1604 cmd_max_conns_host() {
1605 g_object_set(G_OBJECT(uzbl.net.soup_session),
1606 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1611 soup_session_remove_feature
1612 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1613 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1614 /*g_free(uzbl.net.soup_logger);*/
1616 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1617 soup_session_add_feature(uzbl.net.soup_session,
1618 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1623 return webkit_web_view_get_settings(uzbl.gui.web_view);
1628 WebKitWebSettings *ws = view_settings();
1629 if (uzbl.behave.font_size > 0) {
1630 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1633 if (uzbl.behave.monospace_size > 0) {
1634 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1635 uzbl.behave.monospace_size, NULL);
1637 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1638 uzbl.behave.font_size, NULL);
1643 cmd_default_font_family() {
1644 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1645 uzbl.behave.default_font_family, NULL);
1649 cmd_monospace_font_family() {
1650 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1651 uzbl.behave.monospace_font_family, NULL);
1655 cmd_sans_serif_font_family() {
1656 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1657 uzbl.behave.sans_serif_font_family, NULL);
1661 cmd_serif_font_family() {
1662 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1663 uzbl.behave.serif_font_family, NULL);
1667 cmd_cursive_font_family() {
1668 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1669 uzbl.behave.cursive_font_family, NULL);
1673 cmd_fantasy_font_family() {
1674 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1675 uzbl.behave.fantasy_font_family, NULL);
1680 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1684 cmd_disable_plugins() {
1685 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1686 !uzbl.behave.disable_plugins, NULL);
1690 cmd_disable_scripts() {
1691 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1692 !uzbl.behave.disable_scripts, NULL);
1696 cmd_minimum_font_size() {
1697 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1698 uzbl.behave.minimum_font_size, NULL);
1701 cmd_autoload_img() {
1702 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1703 uzbl.behave.autoload_img, NULL);
1708 cmd_autoshrink_img() {
1709 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1710 uzbl.behave.autoshrink_img, NULL);
1715 cmd_enable_spellcheck() {
1716 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1717 uzbl.behave.enable_spellcheck, NULL);
1721 cmd_enable_private() {
1722 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1723 uzbl.behave.enable_private, NULL);
1728 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1729 uzbl.behave.print_bg, NULL);
1734 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1735 uzbl.behave.style_uri, NULL);
1739 cmd_resizable_txt() {
1740 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1741 uzbl.behave.resizable_txt, NULL);
1745 cmd_default_encoding() {
1746 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1747 uzbl.behave.default_encoding, NULL);
1751 cmd_enforce_96dpi() {
1752 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1753 uzbl.behave.enforce_96dpi, NULL);
1757 cmd_caret_browsing() {
1758 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1759 uzbl.behave.caret_browsing, NULL);
1763 cmd_cookie_handler() {
1764 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1765 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1766 if ((g_strcmp0(split[0], "sh") == 0) ||
1767 (g_strcmp0(split[0], "spawn") == 0)) {
1768 g_free (uzbl.behave.cookie_handler);
1769 uzbl.behave.cookie_handler =
1770 g_strdup_printf("sync_%s %s", split[0], split[1]);
1777 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1778 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1779 if ((g_strcmp0(split[0], "sh") == 0) ||
1780 (g_strcmp0(split[0], "spawn") == 0)) {
1781 g_free (uzbl.behave.new_window);
1782 uzbl.behave.new_window =
1783 g_strdup_printf("%s %s", split[0], split[1]);
1790 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1795 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1800 if(uzbl.behave.inject_html) {
1801 webkit_web_view_load_html_string (uzbl.gui.web_view,
1802 uzbl.behave.inject_html, NULL);
1811 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1812 uzbl.behave.modmask = 0;
1814 if(uzbl.behave.modkey)
1815 g_free(uzbl.behave.modkey);
1816 uzbl.behave.modkey = buf;
1818 for (i = 0; modkeys[i].key != NULL; i++) {
1819 if (g_strrstr(buf, modkeys[i].key))
1820 uzbl.behave.modmask |= modkeys[i].mask;
1826 if (*uzbl.net.useragent == ' ') {
1827 g_free (uzbl.net.useragent);
1828 uzbl.net.useragent = NULL;
1830 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1831 uzbl.net.useragent, NULL);
1837 gtk_widget_ref(uzbl.gui.scrolled_win);
1838 gtk_widget_ref(uzbl.gui.mainbar);
1839 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1840 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1842 if(uzbl.behave.status_top) {
1843 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1844 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1847 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1848 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1850 gtk_widget_unref(uzbl.gui.scrolled_win);
1851 gtk_widget_unref(uzbl.gui.mainbar);
1852 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1857 set_var_value(const gchar *name, gchar *val) {
1858 uzbl_cmdprop *c = NULL;
1862 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1863 if(!c->writeable) return FALSE;
1865 /* check for the variable type */
1866 if (c->type == TYPE_STR) {
1867 buf = expand(val, 0);
1870 } else if(c->type == TYPE_INT) {
1871 buf = expand(val, 0);
1872 *c->ptr.i = (int)strtoul(buf, &endp, 10);
1874 } else if (c->type == TYPE_FLOAT) {
1875 buf = expand(val, 0);
1876 *c->ptr.f = strtod(buf, &endp);
1880 /* invoke a command specific function */
1881 if(c->func) c->func();
1888 Behaviour *b = &uzbl.behave;
1890 if(b->html_buffer->str) {
1891 webkit_web_view_load_html_string (uzbl.gui.web_view,
1892 b->html_buffer->str, b->base_url);
1893 g_string_free(b->html_buffer, TRUE);
1894 b->html_buffer = g_string_new("");
1898 enum {M_CMD, M_HTML};
1900 parse_cmd_line(const char *ctl_line, GString *result) {
1901 Behaviour *b = &uzbl.behave;
1904 if(b->mode == M_HTML) {
1905 len = strlen(b->html_endmarker);
1906 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1907 if(len == strlen(ctl_line)-1 &&
1908 !strncmp(b->html_endmarker, ctl_line, len)) {
1910 set_var_value("mode", "0");
1915 set_timeout(b->html_timeout);
1916 g_string_append(b->html_buffer, ctl_line);
1919 else if((ctl_line[0] == '#') /* Comments */
1920 || (ctl_line[0] == ' ')
1921 || (ctl_line[0] == '\n'))
1922 ; /* ignore these lines */
1923 else { /* parse a command */
1925 gchar **tokens = NULL;
1926 len = strlen(ctl_line);
1928 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1929 ctlstrip = g_strndup(ctl_line, len - 1);
1930 else ctlstrip = g_strdup(ctl_line);
1932 tokens = g_strsplit(ctlstrip, " ", 2);
1933 parse_command(tokens[0], tokens[1], result);
1940 build_stream_name(int type, const gchar* dir) {
1941 State *s = &uzbl.state;
1945 str = g_strdup_printf
1946 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1947 } else if (type == SOCKET) {
1948 str = g_strdup_printf
1949 ("%s/uzbl_socket_%s", dir, s->instance_name);
1955 control_fifo(GIOChannel *gio, GIOCondition condition) {
1956 if (uzbl.state.verbose)
1957 printf("triggered\n");
1962 if (condition & G_IO_HUP)
1963 g_error ("Fifo: Read end of pipe died!\n");
1966 g_error ("Fifo: GIOChannel broke\n");
1968 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1969 if (ret == G_IO_STATUS_ERROR) {
1970 g_error ("Fifo: Error reading: %s\n", err->message);
1974 parse_cmd_line(ctl_line, NULL);
1981 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1982 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1983 if (unlink(uzbl.comm.fifo_path) == -1)
1984 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1985 g_free(uzbl.comm.fifo_path);
1986 uzbl.comm.fifo_path = NULL;
1989 GIOChannel *chan = NULL;
1990 GError *error = NULL;
1991 gchar *path = build_stream_name(FIFO, dir);
1993 if (!file_exists(path)) {
1994 if (mkfifo (path, 0666) == 0) {
1995 // 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.
1996 chan = g_io_channel_new_file(path, "r+", &error);
1998 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1999 if (uzbl.state.verbose)
2000 printf ("init_fifo: created successfully as %s\n", path);
2001 uzbl.comm.fifo_path = path;
2003 } else g_warning ("init_fifo: could not add watch on %s\n", path);
2004 } else g_warning ("init_fifo: can't open: %s\n", error->message);
2005 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
2006 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
2008 /* if we got this far, there was an error; cleanup */
2009 if (error) g_error_free (error);
2016 control_stdin(GIOChannel *gio, GIOCondition condition) {
2018 gchar *ctl_line = NULL;
2021 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
2022 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
2025 parse_cmd_line(ctl_line, NULL);
2033 GIOChannel *chan = NULL;
2034 GError *error = NULL;
2036 chan = g_io_channel_unix_new(fileno(stdin));
2038 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2039 g_error ("Stdin: could not add watch\n");
2041 if (uzbl.state.verbose)
2042 printf ("Stdin: watch added successfully\n");
2045 g_error ("Stdin: Error while opening: %s\n", error->message);
2047 if (error) g_error_free (error);
2051 control_socket(GIOChannel *chan) {
2052 struct sockaddr_un remote;
2053 unsigned int t = sizeof(remote);
2055 GIOChannel *clientchan;
2057 clientsock = accept (g_io_channel_unix_get_fd(chan),
2058 (struct sockaddr *) &remote, &t);
2060 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2061 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2062 (GIOFunc) control_client_socket, clientchan);
2069 control_client_socket(GIOChannel *clientchan) {
2071 GString *result = g_string_new("");
2072 GError *error = NULL;
2076 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2077 if (ret == G_IO_STATUS_ERROR) {
2078 g_warning ("Error reading: %s\n", error->message);
2079 g_io_channel_shutdown(clientchan, TRUE, &error);
2081 } else if (ret == G_IO_STATUS_EOF) {
2082 /* shutdown and remove channel watch from main loop */
2083 g_io_channel_shutdown(clientchan, TRUE, &error);
2088 parse_cmd_line (ctl_line, result);
2089 g_string_append_c(result, '\n');
2090 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2092 if (ret == G_IO_STATUS_ERROR) {
2093 g_warning ("Error writing: %s", error->message);
2095 g_io_channel_flush(clientchan, &error);
2098 if (error) g_error_free (error);
2099 g_string_free(result, TRUE);
2105 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2106 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2107 if (unlink(uzbl.comm.socket_path) == -1)
2108 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2109 g_free(uzbl.comm.socket_path);
2110 uzbl.comm.socket_path = NULL;
2118 GIOChannel *chan = NULL;
2120 struct sockaddr_un local;
2121 gchar *path = build_stream_name(SOCKET, dir);
2123 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2125 local.sun_family = AF_UNIX;
2126 strcpy (local.sun_path, path);
2127 unlink (local.sun_path);
2129 len = strlen (local.sun_path) + sizeof (local.sun_family);
2130 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2131 if (uzbl.state.verbose)
2132 printf ("init_socket: opened in %s\n", path);
2135 if( (chan = g_io_channel_unix_new(sock)) ) {
2136 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2137 uzbl.comm.socket_path = path;
2140 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2142 /* if we got this far, there was an error; cleanup */
2149 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2150 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2152 // this function may be called very early when the templates are not set (yet), hence the checks
2154 update_title (void) {
2155 Behaviour *b = &uzbl.behave;
2158 if (b->show_status) {
2159 if (b->title_format_short) {
2160 parsed = expand(b->title_format_short, 0);
2161 if (uzbl.gui.main_window)
2162 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2165 if (b->status_format) {
2166 parsed = expand(b->status_format, 0);
2167 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2170 if (b->status_background) {
2172 gdk_color_parse (b->status_background, &color);
2173 //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)
2174 if (uzbl.gui.main_window)
2175 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2176 else if (uzbl.gui.plug)
2177 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2180 if (b->title_format_long) {
2181 parsed = expand(b->title_format_long, 0);
2182 if (uzbl.gui.main_window)
2183 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2190 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2194 retreive_geometry();
2199 key_press_cb (GtkWidget* window, GdkEventKey* event)
2201 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2205 if (event->type != GDK_KEY_PRESS ||
2206 event->keyval == GDK_Page_Up ||
2207 event->keyval == GDK_Page_Down ||
2208 event->keyval == GDK_Up ||
2209 event->keyval == GDK_Down ||
2210 event->keyval == GDK_Left ||
2211 event->keyval == GDK_Right ||
2212 event->keyval == GDK_Shift_L ||
2213 event->keyval == GDK_Shift_R)
2216 /* turn off insert mode (if always_insert_mode is not used) */
2217 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2218 set_insert_mode(uzbl.behave.always_insert_mode);
2223 if (uzbl.behave.insert_mode &&
2224 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2225 (!uzbl.behave.modmask)
2230 if (event->keyval == GDK_Escape) {
2233 dehilight(uzbl.gui.web_view, NULL, NULL);
2237 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2238 if (event->keyval == GDK_Insert) {
2240 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2241 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2243 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2246 GString* keycmd = g_string_new(uzbl.state.keycmd);
2247 g_string_append (keycmd, str);
2248 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2255 if (event->keyval == GDK_BackSpace)
2256 keycmd_bs(NULL, NULL, NULL);
2258 gboolean key_ret = FALSE;
2259 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2262 GString* keycmd = g_string_new(uzbl.state.keycmd);
2263 g_string_append(keycmd, event->string);
2264 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2267 run_keycmd(key_ret);
2269 if (key_ret) return (!uzbl.behave.insert_mode);
2274 run_keycmd(const gboolean key_ret) {
2275 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2277 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2279 parse_command(act->name, act->param, NULL);
2283 /* try if it's an incremental keycmd or one that takes args, and run it */
2284 GString* short_keys = g_string_new ("");
2285 GString* short_keys_inc = g_string_new ("");
2287 guint len = strlen(uzbl.state.keycmd);
2288 for (i=0; i<len; i++) {
2289 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2290 g_string_assign(short_keys_inc, short_keys->str);
2291 g_string_append_c(short_keys, '_');
2292 g_string_append_c(short_keys_inc, '*');
2294 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2295 /* run normal cmds only if return was pressed */
2296 exec_paramcmd(act, i);
2299 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2300 if (key_ret) /* just quit the incremental command on return */
2302 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2306 g_string_truncate(short_keys, short_keys->len - 1);
2308 g_string_free (short_keys, TRUE);
2309 g_string_free (short_keys_inc, TRUE);
2313 exec_paramcmd(const Action *act, const guint i) {
2314 GString *parampart = g_string_new (uzbl.state.keycmd);
2315 GString *actionname = g_string_new ("");
2316 GString *actionparam = g_string_new ("");
2317 g_string_erase (parampart, 0, i+1);
2319 g_string_printf (actionname, act->name, parampart->str);
2321 g_string_printf (actionparam, act->param, parampart->str);
2322 parse_command(actionname->str, actionparam->str, NULL);
2323 g_string_free(actionname, TRUE);
2324 g_string_free(actionparam, TRUE);
2325 g_string_free(parampart, TRUE);
2333 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2335 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2336 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2337 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2338 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2339 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2340 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2341 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2342 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2343 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2344 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2345 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2352 g->mainbar = gtk_hbox_new (FALSE, 0);
2354 /* keep a reference to the bar so we can re-pack it at runtime*/
2355 //sbar_ref = g_object_ref(g->mainbar);
2357 g->mainbar_label = gtk_label_new ("");
2358 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2359 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2360 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2361 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2362 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2363 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2369 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2370 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2371 gtk_widget_set_name (window, "Uzbl browser");
2372 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2373 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2374 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2381 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2382 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2383 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2390 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2392 If actname is one that calls an external command, this function will inject
2393 newargs in front of the user-provided args in that command line. They will
2394 come become after the body of the script (in sh) or after the name of
2395 the command to execute (in spawn).
2396 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2397 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2399 The return value consist of two strings: the action (sh, ...) and its args.
2401 If act is not one that calls an external command, then the given action merely
2404 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2405 /* Arrr! Here be memory leaks */
2406 gchar *actdup = g_strdup(actname);
2407 g_array_append_val(rets, actdup);
2409 if ((g_strcmp0(actname, "spawn") == 0) ||
2410 (g_strcmp0(actname, "sh") == 0) ||
2411 (g_strcmp0(actname, "sync_spawn") == 0) ||
2412 (g_strcmp0(actname, "sync_sh") == 0) ||
2413 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2415 GString *a = g_string_new("");
2416 gchar **spawnparts = split_quoted(origargs, FALSE);
2417 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2418 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2420 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2421 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2423 g_array_append_val(rets, a->str);
2424 g_string_free(a, FALSE);
2425 g_strfreev(spawnparts);
2427 gchar *origdup = g_strdup(origargs);
2428 g_array_append_val(rets, origdup);
2430 return (gchar**)g_array_free(rets, FALSE);
2434 run_handler (const gchar *act, const gchar *args) {
2435 /* Consider this code a temporary hack to make the handlers usable.
2436 In practice, all this splicing, injection, and reconstruction is
2437 inefficient, annoying and hard to manage. Potential pitfalls arise
2438 when the handler specific args 1) are not quoted (the handler
2439 callbacks should take care of this) 2) are quoted but interfere
2440 with the users' own quotation. A more ideal solution is
2441 to refactor parse_command so that it doesn't just take a string
2442 and execute it; rather than that, we should have a function which
2443 returns the argument vector parsed from the string. This vector
2444 could be modified (e.g. insert additional args into it) before
2445 passing it to the next function that actually executes it. Though
2446 it still isn't perfect for chain actions.. will reconsider & re-
2447 factor when I have the time. -duc */
2449 char **parts = g_strsplit(act, " ", 2);
2451 if (g_strcmp0(parts[0], "chain") == 0) {
2452 GString *newargs = g_string_new("");
2453 gchar **chainparts = split_quoted(parts[1], FALSE);
2455 /* for every argument in the chain, inject the handler args
2456 and make sure the new parts are wrapped in quotes */
2457 gchar **cp = chainparts;
2459 gchar *quotless = NULL;
2460 gchar **spliced_quotless = NULL; // sigh -_-;
2461 gchar **inpart = NULL;
2464 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2466 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2467 } else quotless = g_strdup(*cp);
2469 spliced_quotless = g_strsplit(quotless, " ", 2);
2470 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2471 g_strfreev(spliced_quotless);
2473 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2479 parse_command(parts[0], &(newargs->str[1]), NULL);
2480 g_string_free(newargs, TRUE);
2481 g_strfreev(chainparts);
2484 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2485 parse_command(inparts[0], inparts[1], NULL);
2493 add_binding (const gchar *key, const gchar *act) {
2494 char **parts = g_strsplit(act, " ", 2);
2501 if (uzbl.state.verbose)
2502 printf ("Binding %-10s : %s\n", key, act);
2503 action = new_action(parts[0], parts[1]);
2505 if (g_hash_table_remove (uzbl.bindings, key))
2506 g_warning ("Overwriting existing binding for \"%s\"", key);
2507 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2512 get_xdg_var (XDG_Var xdg) {
2513 const gchar* actual_value = getenv (xdg.environmental);
2514 const gchar* home = getenv ("HOME");
2515 gchar* return_value;
2517 if (! actual_value || strcmp (actual_value, "") == 0) {
2518 if (xdg.default_value) {
2519 return_value = str_replace ("~", home, xdg.default_value);
2521 return_value = NULL;
2524 return_value = str_replace("~", home, actual_value);
2527 return return_value;
2531 find_xdg_file (int xdg_type, const char* filename) {
2532 /* xdg_type = 0 => config
2533 xdg_type = 1 => data
2534 xdg_type = 2 => cache*/
2536 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2537 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2540 gchar* temporary_string;
2544 if (! file_exists (temporary_file) && xdg_type != 2) {
2545 buf = get_xdg_var (XDG[3 + xdg_type]);
2546 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2549 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2550 g_free (temporary_file);
2551 temporary_file = g_strconcat (temporary_string, filename, NULL);
2555 //g_free (temporary_string); - segfaults.
2557 if (file_exists (temporary_file)) {
2558 return temporary_file;
2560 g_free(temporary_file);
2566 State *s = &uzbl.state;
2567 Network *n = &uzbl.net;
2569 for (i = 0; default_config[i].command != NULL; i++) {
2570 parse_cmd_line(default_config[i].command, NULL);
2573 if (g_strcmp0(s->config_file, "-") == 0) {
2574 s->config_file = NULL;
2578 else if (!s->config_file) {
2579 s->config_file = find_xdg_file (0, "/uzbl/config");
2582 if (s->config_file) {
2583 GArray* lines = read_file_by_line (s->config_file);
2587 while ((line = g_array_index(lines, gchar*, i))) {
2588 parse_cmd_line (line, NULL);
2592 g_array_free (lines, TRUE);
2594 if (uzbl.state.verbose)
2595 printf ("No configuration file loaded.\n");
2598 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2601 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2604 if (!uzbl.behave.cookie_handler)
2607 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2608 GString *s = g_string_new ("");
2609 SoupURI * soup_uri = soup_message_get_uri(msg);
2610 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2611 run_handler(uzbl.behave.cookie_handler, s->str);
2613 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2614 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2615 if ( p != NULL ) *p = '\0';
2616 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2618 if (uzbl.comm.sync_stdout)
2619 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2621 g_string_free(s, TRUE);
2625 save_cookies (SoupMessage *msg, gpointer user_data){
2629 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2630 cookie = soup_cookie_to_set_cookie_header(ck->data);
2631 SoupURI * soup_uri = soup_message_get_uri(msg);
2632 GString *s = g_string_new ("");
2633 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2634 run_handler(uzbl.behave.cookie_handler, s->str);
2636 g_string_free(s, TRUE);
2641 /* --- WEBINSPECTOR --- */
2643 hide_window_cb(GtkWidget *widget, gpointer data) {
2646 gtk_widget_hide(widget);
2650 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2653 (void) web_inspector;
2654 GtkWidget* scrolled_window;
2655 GtkWidget* new_web_view;
2658 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2659 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2660 G_CALLBACK(hide_window_cb), NULL);
2662 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2663 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2664 gtk_widget_show(g->inspector_window);
2666 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2667 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2668 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2669 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2670 gtk_widget_show(scrolled_window);
2672 new_web_view = webkit_web_view_new();
2673 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2675 return WEBKIT_WEB_VIEW(new_web_view);
2679 inspector_show_window_cb (WebKitWebInspector* inspector){
2681 gtk_widget_show(uzbl.gui.inspector_window);
2685 /* TODO: Add variables and code to make use of these functions */
2687 inspector_close_window_cb (WebKitWebInspector* inspector){
2693 inspector_attach_window_cb (WebKitWebInspector* inspector){
2699 inspector_detach_window_cb (WebKitWebInspector* inspector){
2705 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2711 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2717 set_up_inspector() {
2719 WebKitWebSettings *settings = view_settings();
2720 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2722 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2723 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2724 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2725 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2726 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2727 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2728 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2730 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2734 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2736 uzbl_cmdprop *c = v;
2741 if(c->type == TYPE_STR)
2742 printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2743 else if(c->type == TYPE_INT)
2744 printf("set %s = %d\n", (char *)k, *c->ptr.i);
2745 else if(c->type == TYPE_FLOAT)
2746 printf("set %s = %f\n", (char *)k, *c->ptr.f);
2750 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2754 printf("bind %s = %s %s\n", (char *)k ,
2755 (char *)a->name, a->param?(char *)a->param:"");
2760 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2761 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2765 retreive_geometry() {
2767 GString *buf = g_string_new("");
2769 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2770 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2772 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2774 if(uzbl.gui.geometry)
2775 g_free(uzbl.gui.geometry);
2776 uzbl.gui.geometry = g_string_free(buf, FALSE);
2779 /* set up gtk, gobject, variable defaults and other things that tests and other
2780 * external applications need to do anyhow */
2782 initialize(int argc, char *argv[]) {
2783 if (!g_thread_supported ())
2784 g_thread_init (NULL);
2785 uzbl.state.executable_path = g_strdup(argv[0]);
2786 uzbl.state.selected_url = NULL;
2787 uzbl.state.searchtx = NULL;
2789 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2790 g_option_context_add_main_entries (context, entries, NULL);
2791 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2792 g_option_context_parse (context, &argc, &argv, NULL);
2793 g_option_context_free(context);
2795 if (uzbl.behave.print_version) {
2796 printf("Commit: %s\n", COMMIT);
2800 /* initialize hash table */
2801 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2803 uzbl.net.soup_session = webkit_get_default_session();
2804 uzbl.state.keycmd = g_strdup("");
2806 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2807 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2808 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2809 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2810 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2811 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2813 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2814 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2815 uzbl.gui.sbar.progress_w = 10;
2817 /* HTML mode defaults*/
2818 uzbl.behave.html_buffer = g_string_new("");
2819 uzbl.behave.html_endmarker = g_strdup(".");
2820 uzbl.behave.html_timeout = 60;
2821 uzbl.behave.base_url = g_strdup("http://invalid");
2823 /* default mode indicators */
2824 uzbl.behave.insert_indicator = g_strdup("I");
2825 uzbl.behave.cmd_indicator = g_strdup("C");
2827 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2828 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2829 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2830 uzbl.info.arch = ARCH;
2831 uzbl.info.commit = COMMIT;
2834 make_var_to_name_hash();
2839 #ifndef UZBL_LIBRARY
2842 main (int argc, char* argv[]) {
2843 initialize(argc, argv);
2845 gtk_init (&argc, &argv);
2847 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2848 //main_window_ref = g_object_ref(scrolled_window);
2849 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2850 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2852 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2853 GTK_WIDGET (uzbl.gui.web_view));
2855 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2859 /* initial packing */
2860 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2861 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2863 if (uzbl.state.socket_id) {
2864 uzbl.gui.plug = create_plug ();
2865 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2866 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2868 uzbl.gui.main_window = create_window ();
2869 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2870 gtk_widget_show_all (uzbl.gui.main_window);
2871 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2874 if(!uzbl.state.instance_name)
2875 uzbl.state.instance_name = itos((int)uzbl.xwin);
2877 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2879 if (uzbl.state.verbose) {
2880 printf("Uzbl start location: %s\n", argv[0]);
2881 if (uzbl.state.socket_id)
2882 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2884 printf("window_id %i\n",(int) uzbl.xwin);
2885 printf("pid %i\n", getpid ());
2886 printf("name: %s\n", uzbl.state.instance_name);
2889 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2890 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2891 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2892 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2893 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2895 if(uzbl.gui.geometry)
2898 retreive_geometry();
2900 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2901 if (argc > 1 && !uzbl.state.uri)
2902 uri_override = g_strdup(argv[1]);
2903 gboolean verbose_override = uzbl.state.verbose;
2906 set_insert_mode(FALSE);
2908 if (!uzbl.behave.show_status)
2909 gtk_widget_hide(uzbl.gui.mainbar);
2916 if (verbose_override > uzbl.state.verbose)
2917 uzbl.state.verbose = verbose_override;
2920 set_var_value("uri", uri_override);
2921 g_free(uri_override);
2922 } else if (uzbl.state.uri)
2928 return EXIT_SUCCESS;
2932 /* vi: set et ts=4: */