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>
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { NULL, 0, 0, 0, NULL, NULL, NULL }
81 /* associate command names to their properties */
82 typedef const struct {
89 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
91 /* an abbreviation to help keep the table's width humane */
92 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
97 } var_name_to_ptr[] = {
98 /* variable name pointer to variable in code type dump callback function */
99 /* --------------------------------------------------------------------------------------- */
100 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
101 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
102 { "disable_stdin", PTR(uzbl.behave.disable_stdin, INT, 1, NULL)},
103 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
104 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
105 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
106 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
107 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
108 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
109 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
110 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
111 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
112 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
113 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
114 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
115 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
116 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
117 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
118 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
119 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
120 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
121 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
122 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
123 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
124 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
125 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
126 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
127 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
128 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
129 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
130 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
131 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
132 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
133 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
134 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
135 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
136 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
137 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
138 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
139 /* exported WebKitWebSettings properties */
140 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
141 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
142 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
143 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
144 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
145 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
146 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
147 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
148 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
149 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
150 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
151 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
152 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
153 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
154 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
155 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
157 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
158 }, *n2v_p = var_name_to_ptr;
164 { "SHIFT", GDK_SHIFT_MASK }, // shift
165 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
166 { "CONTROL", GDK_CONTROL_MASK }, // control
167 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
168 { "MOD2", GDK_MOD2_MASK }, // 5th mod
169 { "MOD3", GDK_MOD3_MASK }, // 6th mod
170 { "MOD4", GDK_MOD4_MASK }, // 7th mod
171 { "MOD5", GDK_MOD5_MASK }, // 8th mod
172 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
173 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
174 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
175 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
176 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
177 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
178 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
179 { "META", GDK_META_MASK }, // meta (since 2.10)
184 /* construct a hash from the var_name_to_ptr array for quick access */
186 make_var_to_name_hash() {
187 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
189 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
194 /* --- UTILITY FUNCTIONS --- */
196 expand_vars(char *s) {
199 char ret[256], *vend;
200 GString *buf = g_string_new("");
205 g_string_append_c(buf, *++s);
213 if( (vend = strchr(s, upto)) ||
214 (vend = strchr(s, '\0')) ) {
215 strncpy(ret, s, vend-s);
217 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
218 if(c->type == TYPE_STR)
219 g_string_append(buf, (gchar *)*c->ptr);
220 else if(c->type == TYPE_INT) {
221 char *b = itos((int)*c->ptr);
222 g_string_append(buf, b);
226 if(upto == ' ') s = vend;
232 g_string_append_c(buf, *s);
237 return g_string_free(buf, FALSE);
244 snprintf(tmp, sizeof(tmp), "%i", val);
245 return g_strdup(tmp);
249 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
252 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
255 str_replace (const char* search, const char* replace, const char* string) {
259 buf = g_strsplit (string, search, -1);
260 ret = g_strjoinv (replace, buf);
261 g_strfreev(buf); // somebody said this segfaults
267 read_file_by_line (gchar *path) {
268 GIOChannel *chan = NULL;
269 gchar *readbuf = NULL;
271 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
274 chan = g_io_channel_new_file(path, "r", NULL);
277 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
278 const gchar* val = g_strdup (readbuf);
279 g_array_append_val (lines, val);
284 g_io_channel_unref (chan);
286 fprintf(stderr, "File '%s' not be read.\n", path);
293 gchar* parseenv (char* string) {
294 extern char** environ;
295 gchar* tmpstr = NULL;
299 while (environ[i] != NULL) {
300 gchar** env = g_strsplit (environ[i], "=", 2);
301 gchar* envname = g_strconcat ("$", env[0], NULL);
303 if (g_strrstr (string, envname) != NULL) {
304 tmpstr = g_strdup(string);
306 string = str_replace(envname, env[1], tmpstr);
311 g_strfreev (env); // somebody said this breaks uzbl
319 setup_signal(int signr, sigfunc *shandler) {
320 struct sigaction nh, oh;
322 nh.sa_handler = shandler;
323 sigemptyset(&nh.sa_mask);
326 if(sigaction(signr, &nh, &oh) < 0)
334 if (uzbl.behave.fifo_dir)
335 unlink (uzbl.comm.fifo_path);
336 if (uzbl.behave.socket_dir)
337 unlink (uzbl.comm.socket_path);
339 g_free(uzbl.state.executable_path);
340 g_string_free(uzbl.state.keycmd, TRUE);
341 g_hash_table_destroy(uzbl.bindings);
342 g_hash_table_destroy(uzbl.behave.commands);
345 /* used for html_mode_timeout
346 * be sure to extend this function to use
347 * more timers if needed in other places
350 set_timeout(int seconds) {
352 memset(&t, 0, sizeof t);
354 t.it_value.tv_sec = seconds;
355 t.it_value.tv_usec = 0;
356 setitimer(ITIMER_REAL, &t, NULL);
359 /* --- SIGNAL HANDLER --- */
362 catch_sigterm(int s) {
368 catch_sigint(int s) {
378 set_var_value("mode", "0");
383 /* --- CALLBACKS --- */
386 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
389 (void) navigation_action;
390 (void) policy_decision;
392 const gchar* uri = webkit_network_request_get_uri (request);
393 if (uzbl.state.verbose)
394 printf("New window requested -> %s \n", uri);
395 new_window_load_uri(uri);
400 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
405 /* If we can display it, let's display it... */
406 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
407 webkit_web_policy_decision_use (policy_decision);
411 /* ...everything we can't displayed is downloaded */
412 webkit_web_policy_decision_download (policy_decision);
417 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
421 if (uzbl.state.selected_url != NULL) {
422 if (uzbl.state.verbose)
423 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
424 new_window_load_uri(uzbl.state.selected_url);
426 if (uzbl.state.verbose)
427 printf("New web view -> %s\n","Nothing to open, exiting");
433 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
436 if (uzbl.behave.download_handler) {
437 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
438 if (uzbl.state.verbose)
439 printf("Download -> %s\n",uri);
440 /* if urls not escaped, we may have to escape and quote uri before this call */
441 run_handler(uzbl.behave.download_handler, uri);
446 /* scroll a bar in a given direction */
448 scroll (GtkAdjustment* bar, GArray *argv) {
452 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
453 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
454 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
458 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
459 (void) page; (void) argv; (void) result;
460 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
464 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
465 (void) page; (void) argv; (void) result;
466 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
467 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
471 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
472 (void) page; (void) result;
473 scroll(uzbl.gui.bar_v, argv);
477 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
478 (void) page; (void) result;
479 scroll(uzbl.gui.bar_h, argv);
484 if (!uzbl.behave.show_status) {
485 gtk_widget_hide(uzbl.gui.mainbar);
487 gtk_widget_show(uzbl.gui.mainbar);
493 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
498 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
502 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
507 if (uzbl.behave.show_status) {
508 gtk_widget_hide(uzbl.gui.mainbar);
510 gtk_widget_show(uzbl.gui.mainbar);
512 uzbl.behave.show_status = !uzbl.behave.show_status;
517 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
521 //Set selected_url state variable
522 g_free(uzbl.state.selected_url);
523 uzbl.state.selected_url = NULL;
525 uzbl.state.selected_url = g_strdup(link);
531 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
535 if (uzbl.gui.main_title)
536 g_free (uzbl.gui.main_title);
537 uzbl.gui.main_title = g_strdup (title);
542 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
545 uzbl.gui.sbar.load_progress = progress;
550 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
554 if (uzbl.behave.load_finish_handler)
555 run_handler(uzbl.behave.load_finish_handler, "");
559 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
563 uzbl.gui.sbar.load_progress = 0;
564 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
565 if (uzbl.behave.load_start_handler)
566 run_handler(uzbl.behave.load_start_handler, "");
570 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
573 g_free (uzbl.state.uri);
574 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
575 uzbl.state.uri = g_string_free (newuri, FALSE);
576 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
577 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
580 if (uzbl.behave.load_commit_handler)
581 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
585 destroy_cb (GtkWidget* widget, gpointer data) {
593 if (uzbl.behave.history_handler) {
595 struct tm * timeinfo;
598 timeinfo = localtime ( &rawtime );
599 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
600 run_handler(uzbl.behave.history_handler, date);
605 /* VIEW funcs (little webkit wrappers) */
606 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
608 VIEWFUNC(reload_bypass_cache)
609 VIEWFUNC(stop_loading)
616 /* -- command to callback/function map for things we cannot attach to any signals */
617 static struct {char *key; CommandInfo value;} cmdlist[] =
618 { /* key function no_split */
619 { "back", {view_go_back, 0} },
620 { "forward", {view_go_forward, 0} },
621 { "scroll_vert", {scroll_vert, 0} },
622 { "scroll_horz", {scroll_horz, 0} },
623 { "scroll_begin", {scroll_begin, 0} },
624 { "scroll_end", {scroll_end, 0} },
625 { "reload", {view_reload, 0}, },
626 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
627 { "stop", {view_stop_loading, 0}, },
628 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
629 { "zoom_out", {view_zoom_out, 0}, },
630 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
631 { "uri", {load_uri, TRUE} },
632 { "js", {run_js, TRUE} },
633 { "script", {run_external_js, 0} },
634 { "toggle_status", {toggle_status_cb, 0} },
635 { "spawn", {spawn, 0} },
636 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
637 { "sh", {spawn_sh, 0} },
638 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
639 { "exit", {close_uzbl, 0} },
640 { "search", {search_forward_text, TRUE} },
641 { "search_reverse", {search_reverse_text, TRUE} },
642 { "dehilight", {dehilight, 0} },
643 { "toggle_insert_mode", {toggle_insert_mode, 0} },
644 { "set", {set_var, TRUE} },
645 //{ "get", {get_var, TRUE} },
646 { "bind", {act_bind, TRUE} },
647 { "dump_config", {act_dump_config, 0} },
648 { "keycmd", {keycmd, TRUE} },
649 { "keycmd_nl", {keycmd_nl, TRUE} },
650 { "keycmd_bs", {keycmd_bs, 0} },
651 { "chain", {chain, 0} },
652 { "print", {print, TRUE} }
659 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
661 for (i = 0; i < LENGTH(cmdlist); i++)
662 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
665 /* -- CORE FUNCTIONS -- */
668 free_action(gpointer act) {
669 Action *action = (Action*)act;
670 g_free(action->name);
672 g_free(action->param);
677 new_action(const gchar *name, const gchar *param) {
678 Action *action = g_new(Action, 1);
680 action->name = g_strdup(name);
682 action->param = g_strdup(param);
684 action->param = NULL;
690 file_exists (const char * filename) {
691 return (access(filename, F_OK) == 0);
695 set_var(WebKitWebView *page, GArray *argv, GString *result) {
696 (void) page; (void) result;
697 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
698 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
699 set_var_value(g_strstrip(split[0]), value);
705 print(WebKitWebView *page, GArray *argv, GString *result) {
706 (void) page; (void) result;
709 buf = expand_vars(argv_idx(argv, 0));
710 g_string_assign(result, buf);
715 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
716 (void) page; (void) result;
717 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
718 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
719 add_binding(g_strstrip(split[0]), value);
731 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
732 (void) page; (void) result;
734 if (argv_idx(argv, 0)) {
735 if (strcmp (argv_idx(argv, 0), "0") == 0) {
736 uzbl.behave.insert_mode = FALSE;
738 uzbl.behave.insert_mode = TRUE;
741 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
748 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
751 if (argv_idx(argv, 0)) {
752 GString* newuri = g_string_new (argv_idx(argv, 0));
753 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
754 run_js(web_view, argv, NULL);
757 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
758 g_string_prepend (newuri, "http://");
759 /* if we do handle cookies, ask our handler for them */
760 webkit_web_view_load_uri (web_view, newuri->str);
761 g_string_free (newuri, TRUE);
769 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
770 size_t argumentCount, const JSValueRef arguments[],
771 JSValueRef* exception) {
776 JSStringRef js_result_string;
777 GString *result = g_string_new("");
779 if (argumentCount >= 1) {
780 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
781 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
782 char ctl_line[arg_size];
783 JSStringGetUTF8CString(arg, ctl_line, arg_size);
785 parse_cmd_line(ctl_line, result);
787 JSStringRelease(arg);
789 js_result_string = JSStringCreateWithUTF8CString(result->str);
791 g_string_free(result, TRUE);
793 return JSValueMakeString(ctx, js_result_string);
796 static JSStaticFunction js_static_functions[] = {
797 {"run", js_run_command, kJSPropertyAttributeNone},
802 /* This function creates the class and its definition, only once */
803 if (!uzbl.js.initialized) {
804 /* it would be pretty cool to make this dynamic */
805 uzbl.js.classdef = kJSClassDefinitionEmpty;
806 uzbl.js.classdef.staticFunctions = js_static_functions;
808 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
814 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
815 WebKitWebFrame *frame;
816 JSGlobalContextRef context;
817 JSObjectRef globalobject;
818 JSStringRef var_name;
820 JSStringRef js_script;
821 JSValueRef js_result;
822 JSStringRef js_result_string;
823 size_t js_result_size;
827 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
828 context = webkit_web_frame_get_global_context(frame);
829 globalobject = JSContextGetGlobalObject(context);
831 /* uzbl javascript namespace */
832 var_name = JSStringCreateWithUTF8CString("Uzbl");
833 JSObjectSetProperty(context, globalobject, var_name,
834 JSObjectMake(context, uzbl.js.classref, NULL),
835 kJSClassAttributeNone, NULL);
837 /* evaluate the script and get return value*/
838 js_script = JSStringCreateWithUTF8CString(script);
839 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
840 if (js_result && !JSValueIsUndefined(context, js_result)) {
841 js_result_string = JSValueToStringCopy(context, js_result, NULL);
842 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
844 if (js_result_size) {
845 char js_result_utf8[js_result_size];
846 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
847 g_string_assign(result, js_result_utf8);
850 JSStringRelease(js_result_string);
854 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
856 JSStringRelease(var_name);
857 JSStringRelease(js_script);
861 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
863 if (argv_idx(argv, 0))
864 eval_js(web_view, argv_idx(argv, 0), result);
868 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
870 if (argv_idx(argv, 0)) {
871 GArray* lines = read_file_by_line (argv_idx (argv, 0));
876 while ((line = g_array_index(lines, gchar*, i))) {
878 js = g_strdup (line);
880 gchar* newjs = g_strconcat (js, line, NULL);
887 if (uzbl.state.verbose)
888 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
890 if (argv_idx (argv, 1)) {
891 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
895 eval_js (web_view, js, result);
897 g_array_free (lines, TRUE);
902 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
903 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
904 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
905 webkit_web_view_unmark_text_matches (page);
906 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
907 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
911 if (uzbl.state.searchtx) {
912 if (uzbl.state.verbose)
913 printf ("Searching: %s\n", uzbl.state.searchtx);
914 webkit_web_view_set_highlight_text_matches (page, TRUE);
915 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
920 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
922 search_text(page, argv, TRUE);
926 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
928 search_text(page, argv, FALSE);
932 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
933 (void) argv; (void) result;
934 webkit_web_view_set_highlight_text_matches (page, FALSE);
939 new_window_load_uri (const gchar * uri) {
940 GString* to_execute = g_string_new ("");
941 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
943 for (i = 0; entries[i].long_name != NULL; i++) {
944 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
945 gchar** str = (gchar**)entries[i].arg_data;
947 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
951 if (uzbl.state.verbose)
952 printf("\n%s\n", to_execute->str);
953 g_spawn_command_line_async (to_execute->str, NULL);
954 g_string_free (to_execute, TRUE);
958 chain (WebKitWebView *page, GArray *argv, GString *result) {
959 (void) page; (void) result;
961 gchar **parts = NULL;
963 while ((a = argv_idx(argv, i++))) {
964 parts = g_strsplit (a, " ", 2);
965 parse_command(parts[0], parts[1], result);
971 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
975 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
981 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
985 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
991 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
996 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
998 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1003 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1010 /* --Statusbar functions-- */
1012 build_progressbar_ascii(int percent) {
1013 int width=uzbl.gui.sbar.progress_w;
1016 GString *bar = g_string_new("");
1018 l = (double)percent*((double)width/100.);
1019 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1021 for(i=0; i<(int)l; i++)
1022 g_string_append(bar, uzbl.gui.sbar.progress_s);
1025 g_string_append(bar, uzbl.gui.sbar.progress_u);
1027 return g_string_free(bar, FALSE);
1032 const GScannerConfig scan_config = {
1035 ) /* cset_skip_characters */,
1040 ) /* cset_identifier_first */,
1047 ) /* cset_identifier_nth */,
1048 ( "" ) /* cpair_comment_single */,
1050 TRUE /* case_sensitive */,
1052 FALSE /* skip_comment_multi */,
1053 FALSE /* skip_comment_single */,
1054 FALSE /* scan_comment_multi */,
1055 TRUE /* scan_identifier */,
1056 TRUE /* scan_identifier_1char */,
1057 FALSE /* scan_identifier_NULL */,
1058 TRUE /* scan_symbols */,
1059 FALSE /* scan_binary */,
1060 FALSE /* scan_octal */,
1061 FALSE /* scan_float */,
1062 FALSE /* scan_hex */,
1063 FALSE /* scan_hex_dollar */,
1064 FALSE /* scan_string_sq */,
1065 FALSE /* scan_string_dq */,
1066 TRUE /* numbers_2_int */,
1067 FALSE /* int_2_float */,
1068 FALSE /* identifier_2_string */,
1069 FALSE /* char_2_token */,
1070 FALSE /* symbol_2_token */,
1071 TRUE /* scope_0_fallback */,
1076 uzbl.scan = g_scanner_new(&scan_config);
1077 while(symp->symbol_name) {
1078 g_scanner_scope_add_symbol(uzbl.scan, 0,
1080 GINT_TO_POINTER(symp->symbol_token));
1086 expand_template(const char *template, gboolean escape_markup) {
1087 if(!template) return NULL;
1089 GTokenType token = G_TOKEN_NONE;
1090 GString *ret = g_string_new("");
1094 g_scanner_input_text(uzbl.scan, template, strlen(template));
1095 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1096 token = g_scanner_get_next_token(uzbl.scan);
1098 if(token == G_TOKEN_SYMBOL) {
1099 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1103 buf = uzbl.state.uri?
1104 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1105 g_string_append(ret, buf);
1109 g_string_append(ret, uzbl.state.uri?
1110 uzbl.state.uri:g_strdup(""));
1113 buf = itos(uzbl.gui.sbar.load_progress);
1114 g_string_append(ret, buf);
1117 case SYM_LOADPRGSBAR:
1118 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1119 g_string_append(ret, buf);
1124 buf = uzbl.gui.main_title?
1125 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1126 g_string_append(ret, buf);
1130 g_string_append(ret, uzbl.gui.main_title?
1131 uzbl.gui.main_title:g_strdup(""));
1133 case SYM_SELECTED_URI:
1135 buf = uzbl.state.selected_url?
1136 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1137 g_string_append(ret, buf);
1141 g_string_append(ret, uzbl.state.selected_url?
1142 uzbl.state.selected_url:g_strdup(""));
1145 buf = itos(uzbl.xwin);
1146 g_string_append(ret,
1147 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1152 buf = uzbl.state.keycmd->str?
1153 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1154 g_string_append(ret, buf);
1158 g_string_append(ret, uzbl.state.keycmd->str?
1159 uzbl.state.keycmd->str:g_strdup(""));
1162 g_string_append(ret,
1163 uzbl.behave.insert_mode?
1164 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1167 g_string_append(ret,
1168 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1170 /* useragent syms */
1172 buf = itos(WEBKIT_MAJOR_VERSION);
1173 g_string_append(ret, buf);
1177 buf = itos(WEBKIT_MINOR_VERSION);
1178 g_string_append(ret, buf);
1182 buf = itos(WEBKIT_MICRO_VERSION);
1183 g_string_append(ret, buf);
1187 g_string_append(ret, uzbl.state.unameinfo.sysname);
1190 g_string_append(ret, uzbl.state.unameinfo.nodename);
1193 g_string_append(ret, uzbl.state.unameinfo.release);
1196 g_string_append(ret, uzbl.state.unameinfo.version);
1199 g_string_append(ret, uzbl.state.unameinfo.machine);
1202 g_string_append(ret, ARCH);
1205 case SYM_DOMAINNAME:
1206 g_string_append(ret, uzbl.state.unameinfo.domainname);
1210 g_string_append(ret, COMMIT);
1216 else if(token == G_TOKEN_INT) {
1217 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1218 g_string_append(ret, buf);
1221 else if(token == G_TOKEN_IDENTIFIER) {
1222 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1224 else if(token == G_TOKEN_CHAR) {
1225 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1229 return g_string_free(ret, FALSE);
1231 /* --End Statusbar functions-- */
1234 sharg_append(GArray *a, const gchar *str) {
1235 const gchar *s = (str ? str : "");
1236 g_array_append_val(a, s);
1239 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1241 run_command (const gchar *command, const guint npre, const gchar **args,
1242 const gboolean sync, char **output_stdout) {
1243 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1246 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1247 gchar *pid = itos(getpid());
1248 gchar *xwin = itos(uzbl.xwin);
1250 sharg_append(a, command);
1251 for (i = 0; i < npre; i++) /* add n args before the default vars */
1252 sharg_append(a, args[i]);
1253 sharg_append(a, uzbl.state.config_file);
1254 sharg_append(a, pid);
1255 sharg_append(a, xwin);
1256 sharg_append(a, uzbl.comm.fifo_path);
1257 sharg_append(a, uzbl.comm.socket_path);
1258 sharg_append(a, uzbl.state.uri);
1259 sharg_append(a, uzbl.gui.main_title);
1261 for (i = npre; i < g_strv_length((gchar**)args); i++)
1262 sharg_append(a, args[i]);
1266 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1268 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1269 NULL, NULL, output_stdout, NULL, NULL, &err);
1270 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1271 NULL, NULL, NULL, &err);
1273 if (uzbl.state.verbose) {
1274 GString *s = g_string_new("spawned:");
1275 for (i = 0; i < (a->len); i++) {
1276 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1277 g_string_append_printf(s, " %s", qarg);
1280 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1281 printf("%s\n", s->str);
1282 g_string_free(s, TRUE);
1284 printf("Stdout: %s\n", *output_stdout);
1288 g_printerr("error on run_command: %s\n", err->message);
1293 g_array_free (a, TRUE);
1298 split_quoted(const gchar* src, const gboolean unquote) {
1299 /* split on unquoted space, return array of strings;
1300 remove a layer of quotes and backslashes if unquote */
1301 if (!src) return NULL;
1303 gboolean dq = FALSE;
1304 gboolean sq = FALSE;
1305 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1306 GString *s = g_string_new ("");
1310 for (p = src; *p != '\0'; p++) {
1311 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1312 else if (*p == '\\') { g_string_append_c(s, *p++);
1313 g_string_append_c(s, *p); }
1314 else if ((*p == '"') && unquote && !sq) dq = !dq;
1315 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1317 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1318 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1320 else if ((*p == ' ') && !dq && !sq) {
1321 dup = g_strdup(s->str);
1322 g_array_append_val(a, dup);
1323 g_string_truncate(s, 0);
1324 } else g_string_append_c(s, *p);
1326 dup = g_strdup(s->str);
1327 g_array_append_val(a, dup);
1328 ret = (gchar**)a->data;
1329 g_array_free (a, FALSE);
1330 g_string_free (s, TRUE);
1335 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1336 (void)web_view; (void)result;
1337 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1338 if (argv_idx(argv, 0))
1339 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1343 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1344 (void)web_view; (void)result;
1346 if (argv_idx(argv, 0))
1347 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1348 TRUE, &uzbl.comm.sync_stdout);
1352 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1353 (void)web_view; (void)result;
1354 if (!uzbl.behave.shell_cmd) {
1355 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1360 gchar *spacer = g_strdup("");
1361 g_array_insert_val(argv, 1, spacer);
1362 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1364 for (i = 1; i < g_strv_length(cmd); i++)
1365 g_array_prepend_val(argv, cmd[i]);
1367 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1373 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1374 (void)web_view; (void)result;
1375 if (!uzbl.behave.shell_cmd) {
1376 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1381 gchar *spacer = g_strdup("");
1382 g_array_insert_val(argv, 1, spacer);
1383 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1385 for (i = 1; i < g_strv_length(cmd); i++)
1386 g_array_prepend_val(argv, cmd[i]);
1388 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1389 TRUE, &uzbl.comm.sync_stdout);
1395 parse_command(const char *cmd, const char *param, GString *result) {
1398 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1400 gchar **par = split_quoted(param, TRUE);
1401 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1403 if (c->no_split) { /* don't split */
1404 sharg_append(a, param);
1406 for (i = 0; i < g_strv_length(par); i++)
1407 sharg_append(a, par[i]);
1410 if (result == NULL) {
1411 GString *result_print = g_string_new("");
1413 c->function(uzbl.gui.web_view, a, result_print);
1414 if (result_print->len)
1415 printf("%*s\n", result_print->len, result_print->str);
1417 g_string_free(result_print, TRUE);
1419 c->function(uzbl.gui.web_view, a, result);
1422 g_array_free (a, TRUE);
1425 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1432 if(*uzbl.net.proxy_url == ' '
1433 || uzbl.net.proxy_url == NULL) {
1434 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1435 (GType) SOUP_SESSION_PROXY_URI);
1438 suri = soup_uri_new(uzbl.net.proxy_url);
1439 g_object_set(G_OBJECT(uzbl.net.soup_session),
1440 SOUP_SESSION_PROXY_URI,
1442 soup_uri_free(suri);
1449 if(file_exists(uzbl.gui.icon)) {
1450 if (uzbl.gui.main_window)
1451 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1453 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1455 g_free (uzbl.gui.icon);
1460 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1461 g_array_append_val (a, uzbl.state.uri);
1462 load_uri(uzbl.gui.web_view, a, NULL);
1463 g_array_free (a, TRUE);
1467 cmd_always_insert_mode() {
1468 uzbl.behave.insert_mode =
1469 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1475 g_object_set(G_OBJECT(uzbl.net.soup_session),
1476 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1480 cmd_max_conns_host() {
1481 g_object_set(G_OBJECT(uzbl.net.soup_session),
1482 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1487 soup_session_remove_feature
1488 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1489 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1490 /*g_free(uzbl.net.soup_logger);*/
1492 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1493 soup_session_add_feature(uzbl.net.soup_session,
1494 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1497 static WebKitWebSettings*
1499 return webkit_web_view_get_settings(uzbl.gui.web_view);
1504 WebKitWebSettings *ws = view_settings();
1505 if (uzbl.behave.font_size > 0) {
1506 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1509 if (uzbl.behave.monospace_size > 0) {
1510 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1511 uzbl.behave.monospace_size, NULL);
1513 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1514 uzbl.behave.font_size, NULL);
1520 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1524 cmd_disable_plugins() {
1525 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1526 !uzbl.behave.disable_plugins, NULL);
1530 cmd_disable_scripts() {
1531 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1532 !uzbl.behave.disable_scripts, NULL);
1536 cmd_minimum_font_size() {
1537 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1538 uzbl.behave.minimum_font_size, NULL);
1541 cmd_autoload_img() {
1542 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1543 uzbl.behave.autoload_img, NULL);
1548 cmd_autoshrink_img() {
1549 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1550 uzbl.behave.autoshrink_img, NULL);
1555 cmd_enable_spellcheck() {
1556 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1557 uzbl.behave.enable_spellcheck, NULL);
1561 cmd_enable_private() {
1562 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1563 uzbl.behave.enable_private, NULL);
1568 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1569 uzbl.behave.print_bg, NULL);
1574 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1575 uzbl.behave.style_uri, NULL);
1579 cmd_resizable_txt() {
1580 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1581 uzbl.behave.resizable_txt, NULL);
1585 cmd_default_encoding() {
1586 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1587 uzbl.behave.default_encoding, NULL);
1591 cmd_enforce_96dpi() {
1592 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1593 uzbl.behave.enforce_96dpi, NULL);
1597 cmd_caret_browsing() {
1598 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1599 uzbl.behave.caret_browsing, NULL);
1603 cmd_cookie_handler() {
1604 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1605 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1606 if ((g_strcmp0(split[0], "sh") == 0) ||
1607 (g_strcmp0(split[0], "spawn") == 0)) {
1608 g_free (uzbl.behave.cookie_handler);
1609 uzbl.behave.cookie_handler =
1610 g_strdup_printf("sync_%s %s", split[0], split[1]);
1617 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1622 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1627 if(uzbl.behave.inject_html) {
1628 webkit_web_view_load_html_string (uzbl.gui.web_view,
1629 uzbl.behave.inject_html, NULL);
1638 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1639 uzbl.behave.modmask = 0;
1641 if(uzbl.behave.modkey)
1642 g_free(uzbl.behave.modkey);
1643 uzbl.behave.modkey = buf;
1645 for (i = 0; modkeys[i].key != NULL; i++) {
1646 if (g_strrstr(buf, modkeys[i].key))
1647 uzbl.behave.modmask |= modkeys[i].mask;
1653 if (*uzbl.net.useragent == ' ') {
1654 g_free (uzbl.net.useragent);
1655 uzbl.net.useragent = NULL;
1657 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1659 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1660 g_free(uzbl.net.useragent);
1661 uzbl.net.useragent = ua;
1667 gtk_widget_ref(uzbl.gui.scrolled_win);
1668 gtk_widget_ref(uzbl.gui.mainbar);
1669 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1670 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1672 if(uzbl.behave.status_top) {
1673 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1674 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1677 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1678 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1680 gtk_widget_unref(uzbl.gui.scrolled_win);
1681 gtk_widget_unref(uzbl.gui.mainbar);
1682 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1687 set_var_value(gchar *name, gchar *val) {
1688 uzbl_cmdprop *c = NULL;
1692 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1693 /* check for the variable type */
1694 if (c->type == TYPE_STR) {
1695 buf = expand_vars(val);
1698 } else if(c->type == TYPE_INT) {
1699 int *ip = (int *)c->ptr;
1700 buf = expand_vars(val);
1701 *ip = (int)strtoul(buf, &endp, 10);
1703 } else if (c->type == TYPE_FLOAT) {
1704 float *fp = (float *)c->ptr;
1705 buf = expand_vars(val);
1706 *fp = strtod(buf, &endp);
1710 /* invoke a command specific function */
1711 if(c->func) c->func();
1718 Behaviour *b = &uzbl.behave;
1720 if(b->html_buffer->str) {
1721 webkit_web_view_load_html_string (uzbl.gui.web_view,
1722 b->html_buffer->str, b->base_url);
1723 g_string_free(b->html_buffer, TRUE);
1724 b->html_buffer = g_string_new("");
1728 enum {M_CMD, M_HTML};
1730 parse_cmd_line(const char *ctl_line, GString *result) {
1731 Behaviour *b = &uzbl.behave;
1734 if(b->mode == M_HTML) {
1735 len = strlen(b->html_endmarker);
1736 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1737 if(len == strlen(ctl_line)-1 &&
1738 !strncmp(b->html_endmarker, ctl_line, len)) {
1740 set_var_value("mode", "0");
1745 set_timeout(b->html_timeout);
1746 g_string_append(b->html_buffer, ctl_line);
1749 else if((ctl_line[0] == '#') /* Comments */
1750 || (ctl_line[0] == ' ')
1751 || (ctl_line[0] == '\n'))
1752 ; /* ignore these lines */
1753 else { /* parse a command */
1755 gchar **tokens = NULL;
1756 len = strlen(ctl_line);
1758 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1759 ctlstrip = g_strndup(ctl_line, len - 1);
1760 else ctlstrip = g_strdup(ctl_line);
1762 tokens = g_strsplit(ctlstrip, " ", 2);
1763 parse_command(tokens[0], tokens[1], result);
1770 build_stream_name(int type, const gchar* dir) {
1771 char *xwin_str = NULL;
1772 State *s = &uzbl.state;
1775 xwin_str = itos((int)uzbl.xwin);
1777 str = g_strdup_printf
1778 ("%s/uzbl_fifo_%s", dir,
1779 s->instance_name ? s->instance_name : xwin_str);
1780 } else if (type == SOCKET) {
1781 str = g_strdup_printf
1782 ("%s/uzbl_socket_%s", dir,
1783 s->instance_name ? s->instance_name : xwin_str );
1790 control_fifo(GIOChannel *gio, GIOCondition condition) {
1791 if (uzbl.state.verbose)
1792 printf("triggered\n");
1797 if (condition & G_IO_HUP)
1798 g_error ("Fifo: Read end of pipe died!\n");
1801 g_error ("Fifo: GIOChannel broke\n");
1803 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1804 if (ret == G_IO_STATUS_ERROR) {
1805 g_error ("Fifo: Error reading: %s\n", err->message);
1809 parse_cmd_line(ctl_line, NULL);
1816 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1817 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1818 if (unlink(uzbl.comm.fifo_path) == -1)
1819 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1820 g_free(uzbl.comm.fifo_path);
1821 uzbl.comm.fifo_path = NULL;
1824 if (*dir == ' ') { /* space unsets the variable */
1829 GIOChannel *chan = NULL;
1830 GError *error = NULL;
1831 gchar *path = build_stream_name(FIFO, dir);
1833 if (!file_exists(path)) {
1834 if (mkfifo (path, 0666) == 0) {
1835 // 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.
1836 chan = g_io_channel_new_file(path, "r+", &error);
1838 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1839 if (uzbl.state.verbose)
1840 printf ("init_fifo: created successfully as %s\n", path);
1841 uzbl.comm.fifo_path = path;
1843 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1844 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1845 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1846 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1848 /* if we got this far, there was an error; cleanup */
1849 if (error) g_error_free (error);
1856 control_stdin(GIOChannel *gio, GIOCondition condition) {
1858 gchar *ctl_line = NULL;
1861 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1862 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1865 parse_cmd_line(ctl_line, NULL);
1873 GIOChannel *chan = NULL;
1874 GError *error = NULL;
1876 chan = g_io_channel_unix_new(fileno(stdin));
1878 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1879 g_error ("Stdin: could not add watch\n");
1881 if (uzbl.state.verbose)
1882 printf ("Stdin: watch added successfully\n");
1885 g_error ("Stdin: Error while opening: %s\n", error->message);
1887 if (error) g_error_free (error);
1891 control_socket(GIOChannel *chan) {
1892 struct sockaddr_un remote;
1893 unsigned int t = sizeof(remote);
1895 GIOChannel *clientchan;
1897 clientsock = accept (g_io_channel_unix_get_fd(chan),
1898 (struct sockaddr *) &remote, &t);
1900 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1901 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1902 (GIOFunc) control_client_socket, clientchan);
1909 control_client_socket(GIOChannel *clientchan) {
1911 GString *result = g_string_new("");
1912 GError *error = NULL;
1916 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1917 if (ret == G_IO_STATUS_ERROR) {
1918 g_warning ("Error reading: %s\n", error->message);
1919 g_io_channel_shutdown(clientchan, TRUE, &error);
1921 } else if (ret == G_IO_STATUS_EOF) {
1922 /* shutdown and remove channel watch from main loop */
1923 g_io_channel_shutdown(clientchan, TRUE, &error);
1928 parse_cmd_line (ctl_line, result);
1929 g_string_append_c(result, '\n');
1930 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1932 if (ret == G_IO_STATUS_ERROR) {
1933 g_warning ("Error writing: %s", error->message);
1935 g_io_channel_flush(clientchan, &error);
1938 if (error) g_error_free (error);
1939 g_string_free(result, TRUE);
1945 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1946 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1947 if (unlink(uzbl.comm.socket_path) == -1)
1948 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1949 g_free(uzbl.comm.socket_path);
1950 uzbl.comm.socket_path = NULL;
1958 GIOChannel *chan = NULL;
1960 struct sockaddr_un local;
1961 gchar *path = build_stream_name(SOCKET, dir);
1963 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1965 local.sun_family = AF_UNIX;
1966 strcpy (local.sun_path, path);
1967 unlink (local.sun_path);
1969 len = strlen (local.sun_path) + sizeof (local.sun_family);
1970 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1971 if (uzbl.state.verbose)
1972 printf ("init_socket: opened in %s\n", path);
1975 if( (chan = g_io_channel_unix_new(sock)) ) {
1976 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1977 uzbl.comm.socket_path = path;
1980 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1982 /* if we got this far, there was an error; cleanup */
1989 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1990 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1992 // this function may be called very early when the templates are not set (yet), hence the checks
1994 update_title (void) {
1995 Behaviour *b = &uzbl.behave;
1998 if (b->show_status) {
1999 if (b->title_format_short) {
2000 parsed = expand_template(b->title_format_short, FALSE);
2001 if (uzbl.gui.main_window)
2002 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2005 if (b->status_format) {
2006 parsed = expand_template(b->status_format, TRUE);
2007 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2010 if (b->status_background) {
2012 gdk_color_parse (b->status_background, &color);
2013 //labels and hboxes do not draw their own background. applying this on the window is ok as we the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2014 if (uzbl.gui.main_window)
2015 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2018 if (b->title_format_long) {
2019 parsed = expand_template(b->title_format_long, FALSE);
2020 if (uzbl.gui.main_window)
2021 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2028 key_press_cb (GtkWidget* window, GdkEventKey* event)
2030 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2034 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2035 || event->keyval == GDK_Up || event->keyval == GDK_Down || event->keyval == GDK_Left || event->keyval == GDK_Right || event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
2038 /* turn off insert mode (if always_insert_mode is not used) */
2039 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2040 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2045 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2048 if (event->keyval == GDK_Escape) {
2049 g_string_truncate(uzbl.state.keycmd, 0);
2051 dehilight(uzbl.gui.web_view, NULL, NULL);
2055 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2056 if (event->keyval == GDK_Insert) {
2058 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2059 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2061 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2064 g_string_append (uzbl.state.keycmd, str);
2071 if (event->keyval == GDK_BackSpace)
2072 keycmd_bs(NULL, NULL, NULL);
2074 gboolean key_ret = FALSE;
2075 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2077 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2079 run_keycmd(key_ret);
2081 if (key_ret) return (!uzbl.behave.insert_mode);
2086 run_keycmd(const gboolean key_ret) {
2087 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2089 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2090 g_string_truncate(uzbl.state.keycmd, 0);
2091 parse_command(act->name, act->param, NULL);
2095 /* try if it's an incremental keycmd or one that takes args, and run it */
2096 GString* short_keys = g_string_new ("");
2097 GString* short_keys_inc = g_string_new ("");
2099 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2100 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2101 g_string_assign(short_keys_inc, short_keys->str);
2102 g_string_append_c(short_keys, '_');
2103 g_string_append_c(short_keys_inc, '*');
2105 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2106 /* run normal cmds only if return was pressed */
2107 exec_paramcmd(act, i);
2108 g_string_truncate(uzbl.state.keycmd, 0);
2110 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2111 if (key_ret) /* just quit the incremental command on return */
2112 g_string_truncate(uzbl.state.keycmd, 0);
2113 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2117 g_string_truncate(short_keys, short_keys->len - 1);
2119 g_string_free (short_keys, TRUE);
2120 g_string_free (short_keys_inc, TRUE);
2124 exec_paramcmd(const Action *act, const guint i) {
2125 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2126 GString *actionname = g_string_new ("");
2127 GString *actionparam = g_string_new ("");
2128 g_string_erase (parampart, 0, i+1);
2130 g_string_printf (actionname, act->name, parampart->str);
2132 g_string_printf (actionparam, act->param, parampart->str);
2133 parse_command(actionname->str, actionparam->str, NULL);
2134 g_string_free(actionname, TRUE);
2135 g_string_free(actionparam, TRUE);
2136 g_string_free(parampart, TRUE);
2144 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2145 //main_window_ref = g_object_ref(scrolled_window);
2146 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2148 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2149 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2151 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2152 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2153 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2154 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2155 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2156 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2157 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2158 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2159 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2160 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2161 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2163 return scrolled_window;
2170 g->mainbar = gtk_hbox_new (FALSE, 0);
2172 /* keep a reference to the bar so we can re-pack it at runtime*/
2173 //sbar_ref = g_object_ref(g->mainbar);
2175 g->mainbar_label = gtk_label_new ("");
2176 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2177 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2178 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2179 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2180 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2181 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2186 GtkWidget* create_window () {
2187 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2188 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2189 gtk_widget_set_name (window, "Uzbl browser");
2190 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2191 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2197 GtkPlug* create_plug () {
2198 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2199 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2200 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2207 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2209 If actname is one that calls an external command, this function will inject
2210 newargs in front of the user-provided args in that command line. They will
2211 come become after the body of the script (in sh) or after the name of
2212 the command to execute (in spawn).
2213 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2214 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2216 The return value consist of two strings: the action (sh, ...) and its args.
2218 If act is not one that calls an external command, then the given action merely
2221 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2222 gchar *actdup = g_strdup(actname);
2223 g_array_append_val(rets, actdup);
2225 if ((g_strcmp0(actname, "spawn") == 0) ||
2226 (g_strcmp0(actname, "sh") == 0) ||
2227 (g_strcmp0(actname, "sync_spawn") == 0) ||
2228 (g_strcmp0(actname, "sync_sh") == 0)) {
2230 GString *a = g_string_new("");
2231 gchar **spawnparts = split_quoted(origargs, FALSE);
2232 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2233 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2235 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2236 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2238 g_array_append_val(rets, a->str);
2239 g_string_free(a, FALSE);
2240 g_strfreev(spawnparts);
2242 gchar *origdup = g_strdup(origargs);
2243 g_array_append_val(rets, origdup);
2245 return (gchar**)g_array_free(rets, FALSE);
2249 run_handler (const gchar *act, const gchar *args) {
2250 /* Consider this code a temporary hack to make the handlers usable.
2251 In practice, all this splicing, injection, and reconstruction is
2252 inefficient, annoying and hard to manage. Potential pitfalls arise
2253 when the handler specific args 1) are not quoted (the handler
2254 callbacks should take care of this) 2) are quoted but interfere
2255 with the users' own quotation. A more ideal solution is
2256 to refactor parse_command so that it doesn't just take a string
2257 and execute it; rather than that, we should have a function which
2258 returns the argument vector parsed from the string. This vector
2259 could be modified (e.g. insert additional args into it) before
2260 passing it to the next function that actually executes it. Though
2261 it still isn't perfect for chain actions.. will reconsider & re-
2262 factor when I have the time. -duc */
2264 char **parts = g_strsplit(act, " ", 2);
2266 if (g_strcmp0(parts[0], "chain") == 0) {
2267 GString *newargs = g_string_new("");
2268 gchar **chainparts = split_quoted(parts[1], FALSE);
2270 /* for every argument in the chain, inject the handler args
2271 and make sure the new parts are wrapped in quotes */
2272 gchar **cp = chainparts;
2274 gchar *quotless = NULL;
2275 gchar **spliced_quotless = NULL; // sigh -_-;
2276 gchar **inpart = NULL;
2279 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2281 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2282 } else quotless = g_strdup(*cp);
2284 spliced_quotless = g_strsplit(quotless, " ", 2);
2285 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2286 g_strfreev(spliced_quotless);
2288 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2294 parse_command(parts[0], &(newargs->str[1]), NULL);
2295 g_string_free(newargs, TRUE);
2296 g_strfreev(chainparts);
2299 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2300 parse_command(inparts[0], inparts[1], NULL);
2308 add_binding (const gchar *key, const gchar *act) {
2309 char **parts = g_strsplit(act, " ", 2);
2316 if (uzbl.state.verbose)
2317 printf ("Binding %-10s : %s\n", key, act);
2318 action = new_action(parts[0], parts[1]);
2320 if (g_hash_table_remove (uzbl.bindings, key))
2321 g_warning ("Overwriting existing binding for \"%s\"", key);
2322 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2327 get_xdg_var (XDG_Var xdg) {
2328 const gchar* actual_value = getenv (xdg.environmental);
2329 const gchar* home = getenv ("HOME");
2330 gchar* return_value;
2332 if (! actual_value || strcmp (actual_value, "") == 0) {
2333 if (xdg.default_value) {
2334 return_value = str_replace ("~", home, xdg.default_value);
2336 return_value = NULL;
2339 return_value = str_replace("~", home, actual_value);
2342 return return_value;
2346 find_xdg_file (int xdg_type, char* filename) {
2347 /* xdg_type = 0 => config
2348 xdg_type = 1 => data
2349 xdg_type = 2 => cache*/
2351 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2352 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2355 gchar* temporary_string;
2359 if (! file_exists (temporary_file) && xdg_type != 2) {
2360 buf = get_xdg_var (XDG[3 + xdg_type]);
2361 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2364 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2365 g_free (temporary_file);
2366 temporary_file = g_strconcat (temporary_string, filename, NULL);
2370 //g_free (temporary_string); - segfaults.
2372 if (file_exists (temporary_file)) {
2373 return temporary_file;
2380 State *s = &uzbl.state;
2381 Network *n = &uzbl.net;
2383 for (i = 0; default_config[i].command != NULL; i++) {
2384 parse_cmd_line(default_config[i].command, NULL);
2387 if (!s->config_file) {
2388 s->config_file = find_xdg_file (0, "/uzbl/config");
2391 if (s->config_file) {
2392 GArray* lines = read_file_by_line (s->config_file);
2396 while ((line = g_array_index(lines, gchar*, i))) {
2397 parse_cmd_line (line, NULL);
2401 g_array_free (lines, TRUE);
2403 if (uzbl.state.verbose)
2404 printf ("No configuration file loaded.\n");
2407 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2410 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2413 if (!uzbl.behave.cookie_handler)
2416 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2417 GString *s = g_string_new ("");
2418 SoupURI * soup_uri = soup_message_get_uri(msg);
2419 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2420 run_handler(uzbl.behave.cookie_handler, s->str);
2422 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2423 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2424 if ( p != NULL ) *p = '\0';
2425 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2427 if (uzbl.comm.sync_stdout)
2428 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2430 g_string_free(s, TRUE);
2434 save_cookies (SoupMessage *msg, gpointer user_data){
2438 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2439 cookie = soup_cookie_to_set_cookie_header(ck->data);
2440 SoupURI * soup_uri = soup_message_get_uri(msg);
2441 GString *s = g_string_new ("");
2442 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2443 run_handler(uzbl.behave.cookie_handler, s->str);
2445 g_string_free(s, TRUE);
2450 /* --- WEBINSPECTOR --- */
2452 hide_window_cb(GtkWidget *widget, gpointer data) {
2455 gtk_widget_hide(widget);
2458 static WebKitWebView*
2459 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2462 (void) web_inspector;
2463 GtkWidget* scrolled_window;
2464 GtkWidget* new_web_view;
2467 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2468 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2469 G_CALLBACK(hide_window_cb), NULL);
2471 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2472 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2473 gtk_widget_show(g->inspector_window);
2475 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2476 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2477 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2478 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2479 gtk_widget_show(scrolled_window);
2481 new_web_view = webkit_web_view_new();
2482 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2484 return WEBKIT_WEB_VIEW(new_web_view);
2488 inspector_show_window_cb (WebKitWebInspector* inspector){
2490 gtk_widget_show(uzbl.gui.inspector_window);
2494 /* TODO: Add variables and code to make use of these functions */
2496 inspector_close_window_cb (WebKitWebInspector* inspector){
2502 inspector_attach_window_cb (WebKitWebInspector* inspector){
2508 inspector_detach_window_cb (WebKitWebInspector* inspector){
2514 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2520 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2526 set_up_inspector() {
2528 WebKitWebSettings *settings = view_settings();
2529 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2531 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2532 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2533 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2534 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2535 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2536 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2537 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2539 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2543 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2545 uzbl_cmdprop *c = v;
2550 if(c->type == TYPE_STR)
2551 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2552 else if(c->type == TYPE_INT)
2553 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2557 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2561 printf("bind %s = %s %s\n", (char *)k ,
2562 (char *)a->name, a->param?(char *)a->param:"");
2567 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2568 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2573 main (int argc, char* argv[]) {
2574 gtk_init (&argc, &argv);
2575 if (!g_thread_supported ())
2576 g_thread_init (NULL);
2577 uzbl.state.executable_path = g_strdup(argv[0]);
2578 uzbl.state.selected_url = NULL;
2579 uzbl.state.searchtx = NULL;
2581 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2582 g_option_context_add_main_entries (context, entries, NULL);
2583 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2584 g_option_context_parse (context, &argc, &argv, NULL);
2585 g_option_context_free(context);
2587 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2588 gboolean verbose_override = uzbl.state.verbose;
2590 /* initialize hash table */
2591 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2593 uzbl.net.soup_session = webkit_get_default_session();
2594 uzbl.state.keycmd = g_string_new("");
2596 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2597 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2598 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2599 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2600 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2601 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2604 if(uname(&uzbl.state.unameinfo) == -1)
2605 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2607 uzbl.gui.sbar.progress_s = g_strdup("=");
2608 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2609 uzbl.gui.sbar.progress_w = 10;
2611 /* HTML mode defaults*/
2612 uzbl.behave.html_buffer = g_string_new("");
2613 uzbl.behave.html_endmarker = g_strdup(".");
2614 uzbl.behave.html_timeout = 60;
2615 uzbl.behave.base_url = g_strdup("http://invalid");
2617 /* default mode indicators */
2618 uzbl.behave.insert_indicator = g_strdup("I");
2619 uzbl.behave.cmd_indicator = g_strdup("C");
2623 make_var_to_name_hash();
2625 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2627 uzbl.gui.scrolled_win = create_browser();
2630 /* initial packing */
2631 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2632 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2634 if (uzbl.state.socket_id) {
2635 uzbl.gui.plug = create_plug ();
2636 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2637 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2639 uzbl.gui.main_window = create_window ();
2640 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2641 gtk_widget_show_all (uzbl.gui.main_window);
2642 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2645 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2647 if (uzbl.state.verbose) {
2648 printf("Uzbl start location: %s\n", argv[0]);
2649 if (uzbl.state.socket_id)
2650 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2652 printf("window_id %i\n",(int) uzbl.xwin);
2653 printf("pid %i\n", getpid ());
2654 printf("name: %s\n", uzbl.state.instance_name);
2657 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2658 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2659 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2660 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2661 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2665 if (!uzbl.behave.show_status)
2666 gtk_widget_hide(uzbl.gui.mainbar);
2673 if (!uzbl.behave.disable_stdin)
2676 if (verbose_override > uzbl.state.verbose)
2677 uzbl.state.verbose = verbose_override;
2680 set_var_value("uri", uri_override);
2681 g_free(uri_override);
2682 } else if (uzbl.state.uri)
2683 cmd_load_uri(uzbl.gui.web_view, NULL);
2688 return EXIT_SUCCESS;
2691 /* vi: set et ts=4: */