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 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
103 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
104 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
105 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
106 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
107 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
108 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
109 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
110 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
111 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
112 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
113 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
114 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
115 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
116 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
117 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
118 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
119 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
120 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
121 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
122 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
123 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
124 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
125 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
126 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
127 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
128 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
129 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
130 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
131 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
132 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
133 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
134 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
135 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
136 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
137 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
138 /* exported WebKitWebSettings properties */
139 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
140 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
141 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
142 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
143 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
144 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
145 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
146 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
147 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
148 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
149 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
150 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
151 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
152 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
153 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
154 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
156 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
157 }, *n2v_p = var_name_to_ptr;
163 { "SHIFT", GDK_SHIFT_MASK }, // shift
164 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
165 { "CONTROL", GDK_CONTROL_MASK }, // control
166 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
167 { "MOD2", GDK_MOD2_MASK }, // 5th mod
168 { "MOD3", GDK_MOD3_MASK }, // 6th mod
169 { "MOD4", GDK_MOD4_MASK }, // 7th mod
170 { "MOD5", GDK_MOD5_MASK }, // 8th mod
171 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
172 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
173 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
174 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
175 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
176 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
177 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
178 { "META", GDK_META_MASK }, // meta (since 2.10)
183 /* construct a hash from the var_name_to_ptr array for quick access */
185 make_var_to_name_hash() {
186 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
188 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
193 /* --- UTILITY FUNCTIONS --- */
195 expand_vars(char *s) {
198 char ret[256], *vend;
199 GString *buf = g_string_new("");
204 g_string_append_c(buf, *++s);
212 if( (vend = strchr(s, upto)) ||
213 (vend = strchr(s, '\0')) ) {
214 strncpy(ret, s, vend-s);
216 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
217 if(c->type == TYPE_STR)
218 g_string_append(buf, (gchar *)*c->ptr);
219 else if(c->type == TYPE_INT) {
220 char *b = itos((int)*c->ptr);
221 g_string_append(buf, b);
225 if(upto == ' ') s = vend;
231 g_string_append_c(buf, *s);
236 return g_string_free(buf, FALSE);
243 snprintf(tmp, sizeof(tmp), "%i", val);
244 return g_strdup(tmp);
248 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
251 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
254 str_replace (const char* search, const char* replace, const char* string) {
258 buf = g_strsplit (string, search, -1);
259 ret = g_strjoinv (replace, buf);
260 g_strfreev(buf); // somebody said this segfaults
266 read_file_by_line (gchar *path) {
267 GIOChannel *chan = NULL;
268 gchar *readbuf = NULL;
270 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
273 chan = g_io_channel_new_file(path, "r", NULL);
276 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
277 const gchar* val = g_strdup (readbuf);
278 g_array_append_val (lines, val);
283 g_io_channel_unref (chan);
285 fprintf(stderr, "File '%s' not be read.\n", path);
292 gchar* parseenv (char* string) {
293 extern char** environ;
294 gchar* tmpstr = NULL;
298 while (environ[i] != NULL) {
299 gchar** env = g_strsplit (environ[i], "=", 2);
300 gchar* envname = g_strconcat ("$", env[0], NULL);
302 if (g_strrstr (string, envname) != NULL) {
303 tmpstr = g_strdup(string);
305 string = str_replace(envname, env[1], tmpstr);
310 g_strfreev (env); // somebody said this breaks uzbl
318 setup_signal(int signr, sigfunc *shandler) {
319 struct sigaction nh, oh;
321 nh.sa_handler = shandler;
322 sigemptyset(&nh.sa_mask);
325 if(sigaction(signr, &nh, &oh) < 0)
333 if (uzbl.behave.fifo_dir)
334 unlink (uzbl.comm.fifo_path);
335 if (uzbl.behave.socket_dir)
336 unlink (uzbl.comm.socket_path);
338 g_free(uzbl.state.executable_path);
339 g_string_free(uzbl.state.keycmd, TRUE);
340 g_hash_table_destroy(uzbl.bindings);
341 g_hash_table_destroy(uzbl.behave.commands);
344 /* used for html_mode_timeout
345 * be sure to extend this function to use
346 * more timers if needed in other places
349 set_timeout(int seconds) {
351 memset(&t, 0, sizeof t);
353 t.it_value.tv_sec = seconds;
354 t.it_value.tv_usec = 0;
355 setitimer(ITIMER_REAL, &t, NULL);
358 /* --- SIGNAL HANDLER --- */
361 catch_sigterm(int s) {
367 catch_sigint(int s) {
377 set_var_value("mode", "0");
382 /* --- CALLBACKS --- */
385 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
388 (void) navigation_action;
389 (void) policy_decision;
391 const gchar* uri = webkit_network_request_get_uri (request);
392 if (uzbl.state.verbose)
393 printf("New window requested -> %s \n", uri);
394 new_window_load_uri(uri);
399 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
404 /* If we can display it, let's display it... */
405 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
406 webkit_web_policy_decision_use (policy_decision);
410 /* ...everything we can't displayed is downloaded */
411 webkit_web_policy_decision_download (policy_decision);
416 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
420 if (uzbl.state.selected_url != NULL) {
421 if (uzbl.state.verbose)
422 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
423 new_window_load_uri(uzbl.state.selected_url);
425 if (uzbl.state.verbose)
426 printf("New web view -> %s\n","Nothing to open, exiting");
432 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
435 if (uzbl.behave.download_handler) {
436 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
437 if (uzbl.state.verbose)
438 printf("Download -> %s\n",uri);
439 /* if urls not escaped, we may have to escape and quote uri before this call */
440 run_handler(uzbl.behave.download_handler, uri);
445 /* scroll a bar in a given direction */
447 scroll (GtkAdjustment* bar, GArray *argv) {
451 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
452 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
453 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
457 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
458 (void) page; (void) argv; (void) result;
459 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
463 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
464 (void) page; (void) argv; (void) result;
465 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
466 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
470 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
471 (void) page; (void) result;
472 scroll(uzbl.gui.bar_v, argv);
476 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
477 (void) page; (void) result;
478 scroll(uzbl.gui.bar_h, argv);
483 if (!uzbl.behave.show_status) {
484 gtk_widget_hide(uzbl.gui.mainbar);
486 gtk_widget_show(uzbl.gui.mainbar);
492 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
497 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
501 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
506 if (uzbl.behave.show_status) {
507 gtk_widget_hide(uzbl.gui.mainbar);
509 gtk_widget_show(uzbl.gui.mainbar);
511 uzbl.behave.show_status = !uzbl.behave.show_status;
516 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
520 //Set selected_url state variable
521 g_free(uzbl.state.selected_url);
522 uzbl.state.selected_url = NULL;
524 uzbl.state.selected_url = g_strdup(link);
530 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
534 if (uzbl.gui.main_title)
535 g_free (uzbl.gui.main_title);
536 uzbl.gui.main_title = g_strdup (title);
541 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
544 uzbl.gui.sbar.load_progress = progress;
549 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
553 if (uzbl.behave.load_finish_handler)
554 run_handler(uzbl.behave.load_finish_handler, "");
558 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
562 uzbl.gui.sbar.load_progress = 0;
563 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
564 if (uzbl.behave.load_start_handler)
565 run_handler(uzbl.behave.load_start_handler, "");
569 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
572 g_free (uzbl.state.uri);
573 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
574 uzbl.state.uri = g_string_free (newuri, FALSE);
575 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
576 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
579 if (uzbl.behave.load_commit_handler)
580 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
584 destroy_cb (GtkWidget* widget, gpointer data) {
592 if (uzbl.behave.history_handler) {
594 struct tm * timeinfo;
597 timeinfo = localtime ( &rawtime );
598 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
599 run_handler(uzbl.behave.history_handler, date);
604 /* VIEW funcs (little webkit wrappers) */
605 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
607 VIEWFUNC(reload_bypass_cache)
608 VIEWFUNC(stop_loading)
615 /* -- command to callback/function map for things we cannot attach to any signals */
616 static struct {char *key; CommandInfo value;} cmdlist[] =
617 { /* key function no_split */
618 { "back", {view_go_back, 0} },
619 { "forward", {view_go_forward, 0} },
620 { "scroll_vert", {scroll_vert, 0} },
621 { "scroll_horz", {scroll_horz, 0} },
622 { "scroll_begin", {scroll_begin, 0} },
623 { "scroll_end", {scroll_end, 0} },
624 { "reload", {view_reload, 0}, },
625 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
626 { "stop", {view_stop_loading, 0}, },
627 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
628 { "zoom_out", {view_zoom_out, 0}, },
629 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
630 { "uri", {load_uri, TRUE} },
631 { "js", {run_js, TRUE} },
632 { "script", {run_external_js, 0} },
633 { "toggle_status", {toggle_status_cb, 0} },
634 { "spawn", {spawn, 0} },
635 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
636 { "sh", {spawn_sh, 0} },
637 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
638 { "exit", {close_uzbl, 0} },
639 { "search", {search_forward_text, TRUE} },
640 { "search_reverse", {search_reverse_text, TRUE} },
641 { "dehilight", {dehilight, 0} },
642 { "toggle_insert_mode", {toggle_insert_mode, 0} },
643 { "set", {set_var, TRUE} },
644 //{ "get", {get_var, TRUE} },
645 { "bind", {act_bind, TRUE} },
646 { "dump_config", {act_dump_config, 0} },
647 { "keycmd", {keycmd, TRUE} },
648 { "keycmd_nl", {keycmd_nl, TRUE} },
649 { "keycmd_bs", {keycmd_bs, 0} },
650 { "chain", {chain, 0} },
651 { "print", {print, TRUE} }
658 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
660 for (i = 0; i < LENGTH(cmdlist); i++)
661 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
664 /* -- CORE FUNCTIONS -- */
667 free_action(gpointer act) {
668 Action *action = (Action*)act;
669 g_free(action->name);
671 g_free(action->param);
676 new_action(const gchar *name, const gchar *param) {
677 Action *action = g_new(Action, 1);
679 action->name = g_strdup(name);
681 action->param = g_strdup(param);
683 action->param = NULL;
689 file_exists (const char * filename) {
690 return (access(filename, F_OK) == 0);
694 set_var(WebKitWebView *page, GArray *argv, GString *result) {
695 (void) page; (void) result;
696 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
697 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
698 set_var_value(g_strstrip(split[0]), value);
704 print(WebKitWebView *page, GArray *argv, GString *result) {
705 (void) page; (void) result;
708 buf = expand_vars(argv_idx(argv, 0));
709 g_string_assign(result, buf);
714 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
715 (void) page; (void) result;
716 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
717 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
718 add_binding(g_strstrip(split[0]), value);
730 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
731 (void) page; (void) result;
733 if (argv_idx(argv, 0)) {
734 if (strcmp (argv_idx(argv, 0), "0") == 0) {
735 uzbl.behave.insert_mode = FALSE;
737 uzbl.behave.insert_mode = TRUE;
740 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
747 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
750 if (argv_idx(argv, 0)) {
751 GString* newuri = g_string_new (argv_idx(argv, 0));
752 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
753 run_js(web_view, argv, NULL);
756 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
757 g_string_prepend (newuri, "http://");
758 /* if we do handle cookies, ask our handler for them */
759 webkit_web_view_load_uri (web_view, newuri->str);
760 g_string_free (newuri, TRUE);
768 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
769 size_t argumentCount, const JSValueRef arguments[],
770 JSValueRef* exception) {
775 JSStringRef js_result_string;
776 GString *result = g_string_new("");
778 if (argumentCount >= 1) {
779 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
780 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
781 char ctl_line[arg_size];
782 JSStringGetUTF8CString(arg, ctl_line, arg_size);
784 parse_cmd_line(ctl_line, result);
786 JSStringRelease(arg);
788 js_result_string = JSStringCreateWithUTF8CString(result->str);
790 g_string_free(result, TRUE);
792 return JSValueMakeString(ctx, js_result_string);
795 static JSStaticFunction js_static_functions[] = {
796 {"run", js_run_command, kJSPropertyAttributeNone},
801 /* This function creates the class and its definition, only once */
802 if (!uzbl.js.initialized) {
803 /* it would be pretty cool to make this dynamic */
804 uzbl.js.classdef = kJSClassDefinitionEmpty;
805 uzbl.js.classdef.staticFunctions = js_static_functions;
807 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
813 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
814 WebKitWebFrame *frame;
815 JSGlobalContextRef context;
816 JSObjectRef globalobject;
817 JSStringRef var_name;
819 JSStringRef js_script;
820 JSValueRef js_result;
821 JSStringRef js_result_string;
822 size_t js_result_size;
826 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
827 context = webkit_web_frame_get_global_context(frame);
828 globalobject = JSContextGetGlobalObject(context);
830 /* uzbl javascript namespace */
831 var_name = JSStringCreateWithUTF8CString("Uzbl");
832 JSObjectSetProperty(context, globalobject, var_name,
833 JSObjectMake(context, uzbl.js.classref, NULL),
834 kJSClassAttributeNone, NULL);
836 /* evaluate the script and get return value*/
837 js_script = JSStringCreateWithUTF8CString(script);
838 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
839 if (js_result && !JSValueIsUndefined(context, js_result)) {
840 js_result_string = JSValueToStringCopy(context, js_result, NULL);
841 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
843 if (js_result_size) {
844 char js_result_utf8[js_result_size];
845 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
846 g_string_assign(result, js_result_utf8);
849 JSStringRelease(js_result_string);
853 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
855 JSStringRelease(var_name);
856 JSStringRelease(js_script);
860 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
862 if (argv_idx(argv, 0))
863 eval_js(web_view, argv_idx(argv, 0), result);
867 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
869 if (argv_idx(argv, 0)) {
870 GArray* lines = read_file_by_line (argv_idx (argv, 0));
875 while ((line = g_array_index(lines, gchar*, i))) {
877 js = g_strdup (line);
879 gchar* newjs = g_strconcat (js, line, NULL);
886 if (uzbl.state.verbose)
887 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
889 if (argv_idx (argv, 1)) {
890 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
894 eval_js (web_view, js, result);
896 g_array_free (lines, TRUE);
901 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
902 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
903 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
904 webkit_web_view_unmark_text_matches (page);
905 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
906 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
910 if (uzbl.state.searchtx) {
911 if (uzbl.state.verbose)
912 printf ("Searching: %s\n", uzbl.state.searchtx);
913 webkit_web_view_set_highlight_text_matches (page, TRUE);
914 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
919 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
921 search_text(page, argv, TRUE);
925 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
927 search_text(page, argv, FALSE);
931 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
932 (void) argv; (void) result;
933 webkit_web_view_set_highlight_text_matches (page, FALSE);
938 new_window_load_uri (const gchar * uri) {
939 GString* to_execute = g_string_new ("");
940 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
942 for (i = 0; entries[i].long_name != NULL; i++) {
943 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
944 gchar** str = (gchar**)entries[i].arg_data;
946 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
950 if (uzbl.state.verbose)
951 printf("\n%s\n", to_execute->str);
952 g_spawn_command_line_async (to_execute->str, NULL);
953 g_string_free (to_execute, TRUE);
957 chain (WebKitWebView *page, GArray *argv, GString *result) {
958 (void) page; (void) result;
960 gchar **parts = NULL;
962 while ((a = argv_idx(argv, i++))) {
963 parts = g_strsplit (a, " ", 2);
964 parse_command(parts[0], parts[1], result);
970 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
974 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
980 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
984 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
990 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
995 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
997 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1002 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1009 /* --Statusbar functions-- */
1011 build_progressbar_ascii(int percent) {
1012 int width=uzbl.gui.sbar.progress_w;
1015 GString *bar = g_string_new("");
1017 l = (double)percent*((double)width/100.);
1018 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1020 for(i=0; i<(int)l; i++)
1021 g_string_append(bar, uzbl.gui.sbar.progress_s);
1024 g_string_append(bar, uzbl.gui.sbar.progress_u);
1026 return g_string_free(bar, FALSE);
1031 const GScannerConfig scan_config = {
1034 ) /* cset_skip_characters */,
1039 ) /* cset_identifier_first */,
1046 ) /* cset_identifier_nth */,
1047 ( "" ) /* cpair_comment_single */,
1049 TRUE /* case_sensitive */,
1051 FALSE /* skip_comment_multi */,
1052 FALSE /* skip_comment_single */,
1053 FALSE /* scan_comment_multi */,
1054 TRUE /* scan_identifier */,
1055 TRUE /* scan_identifier_1char */,
1056 FALSE /* scan_identifier_NULL */,
1057 TRUE /* scan_symbols */,
1058 FALSE /* scan_binary */,
1059 FALSE /* scan_octal */,
1060 FALSE /* scan_float */,
1061 FALSE /* scan_hex */,
1062 FALSE /* scan_hex_dollar */,
1063 FALSE /* scan_string_sq */,
1064 FALSE /* scan_string_dq */,
1065 TRUE /* numbers_2_int */,
1066 FALSE /* int_2_float */,
1067 FALSE /* identifier_2_string */,
1068 FALSE /* char_2_token */,
1069 FALSE /* symbol_2_token */,
1070 TRUE /* scope_0_fallback */,
1075 uzbl.scan = g_scanner_new(&scan_config);
1076 while(symp->symbol_name) {
1077 g_scanner_scope_add_symbol(uzbl.scan, 0,
1079 GINT_TO_POINTER(symp->symbol_token));
1085 expand_template(const char *template, gboolean escape_markup) {
1086 if(!template) return NULL;
1088 GTokenType token = G_TOKEN_NONE;
1089 GString *ret = g_string_new("");
1093 g_scanner_input_text(uzbl.scan, template, strlen(template));
1094 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1095 token = g_scanner_get_next_token(uzbl.scan);
1097 if(token == G_TOKEN_SYMBOL) {
1098 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1102 buf = uzbl.state.uri?
1103 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1104 g_string_append(ret, buf);
1108 g_string_append(ret, uzbl.state.uri?
1109 uzbl.state.uri:g_strdup(""));
1112 buf = itos(uzbl.gui.sbar.load_progress);
1113 g_string_append(ret, buf);
1116 case SYM_LOADPRGSBAR:
1117 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1118 g_string_append(ret, buf);
1123 buf = uzbl.gui.main_title?
1124 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1125 g_string_append(ret, buf);
1129 g_string_append(ret, uzbl.gui.main_title?
1130 uzbl.gui.main_title:g_strdup(""));
1132 case SYM_SELECTED_URI:
1134 buf = uzbl.state.selected_url?
1135 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1136 g_string_append(ret, buf);
1140 g_string_append(ret, uzbl.state.selected_url?
1141 uzbl.state.selected_url:g_strdup(""));
1144 buf = itos(uzbl.xwin);
1145 g_string_append(ret,
1146 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1151 buf = uzbl.state.keycmd->str?
1152 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1153 g_string_append(ret, buf);
1157 g_string_append(ret, uzbl.state.keycmd->str?
1158 uzbl.state.keycmd->str:g_strdup(""));
1161 g_string_append(ret,
1162 uzbl.behave.insert_mode?
1163 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1166 g_string_append(ret,
1167 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1169 /* useragent syms */
1171 buf = itos(WEBKIT_MAJOR_VERSION);
1172 g_string_append(ret, buf);
1176 buf = itos(WEBKIT_MINOR_VERSION);
1177 g_string_append(ret, buf);
1181 buf = itos(WEBKIT_MICRO_VERSION);
1182 g_string_append(ret, buf);
1186 g_string_append(ret, uzbl.state.unameinfo.sysname);
1189 g_string_append(ret, uzbl.state.unameinfo.nodename);
1192 g_string_append(ret, uzbl.state.unameinfo.release);
1195 g_string_append(ret, uzbl.state.unameinfo.version);
1198 g_string_append(ret, uzbl.state.unameinfo.machine);
1201 g_string_append(ret, ARCH);
1204 case SYM_DOMAINNAME:
1205 g_string_append(ret, uzbl.state.unameinfo.domainname);
1209 g_string_append(ret, COMMIT);
1215 else if(token == G_TOKEN_INT) {
1216 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1217 g_string_append(ret, buf);
1220 else if(token == G_TOKEN_IDENTIFIER) {
1221 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1223 else if(token == G_TOKEN_CHAR) {
1224 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1228 return g_string_free(ret, FALSE);
1230 /* --End Statusbar functions-- */
1233 sharg_append(GArray *a, const gchar *str) {
1234 const gchar *s = (str ? str : "");
1235 g_array_append_val(a, s);
1238 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1240 run_command (const gchar *command, const guint npre, const gchar **args,
1241 const gboolean sync, char **output_stdout) {
1242 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1245 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1246 gchar *pid = itos(getpid());
1247 gchar *xwin = itos(uzbl.xwin);
1249 sharg_append(a, command);
1250 for (i = 0; i < npre; i++) /* add n args before the default vars */
1251 sharg_append(a, args[i]);
1252 sharg_append(a, uzbl.state.config_file);
1253 sharg_append(a, pid);
1254 sharg_append(a, xwin);
1255 sharg_append(a, uzbl.comm.fifo_path);
1256 sharg_append(a, uzbl.comm.socket_path);
1257 sharg_append(a, uzbl.state.uri);
1258 sharg_append(a, uzbl.gui.main_title);
1260 for (i = npre; i < g_strv_length((gchar**)args); i++)
1261 sharg_append(a, args[i]);
1265 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1267 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1268 NULL, NULL, output_stdout, NULL, NULL, &err);
1269 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1270 NULL, NULL, NULL, &err);
1272 if (uzbl.state.verbose) {
1273 GString *s = g_string_new("spawned:");
1274 for (i = 0; i < (a->len); i++) {
1275 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1276 g_string_append_printf(s, " %s", qarg);
1279 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1280 printf("%s\n", s->str);
1281 g_string_free(s, TRUE);
1283 printf("Stdout: %s\n", *output_stdout);
1287 g_printerr("error on run_command: %s\n", err->message);
1292 g_array_free (a, TRUE);
1297 split_quoted(const gchar* src, const gboolean unquote) {
1298 /* split on unquoted space, return array of strings;
1299 remove a layer of quotes and backslashes if unquote */
1300 if (!src) return NULL;
1302 gboolean dq = FALSE;
1303 gboolean sq = FALSE;
1304 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1305 GString *s = g_string_new ("");
1309 for (p = src; *p != '\0'; p++) {
1310 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1311 else if (*p == '\\') { g_string_append_c(s, *p++);
1312 g_string_append_c(s, *p); }
1313 else if ((*p == '"') && unquote && !sq) dq = !dq;
1314 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1316 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1317 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1319 else if ((*p == ' ') && !dq && !sq) {
1320 dup = g_strdup(s->str);
1321 g_array_append_val(a, dup);
1322 g_string_truncate(s, 0);
1323 } else g_string_append_c(s, *p);
1325 dup = g_strdup(s->str);
1326 g_array_append_val(a, dup);
1327 ret = (gchar**)a->data;
1328 g_array_free (a, FALSE);
1329 g_string_free (s, TRUE);
1334 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1335 (void)web_view; (void)result;
1336 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1337 if (argv_idx(argv, 0))
1338 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1342 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1343 (void)web_view; (void)result;
1345 if (argv_idx(argv, 0))
1346 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1347 TRUE, &uzbl.comm.sync_stdout);
1351 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1352 (void)web_view; (void)result;
1353 if (!uzbl.behave.shell_cmd) {
1354 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1359 gchar *spacer = g_strdup("");
1360 g_array_insert_val(argv, 1, spacer);
1361 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1363 for (i = 1; i < g_strv_length(cmd); i++)
1364 g_array_prepend_val(argv, cmd[i]);
1366 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1372 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1373 (void)web_view; (void)result;
1374 if (!uzbl.behave.shell_cmd) {
1375 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1380 gchar *spacer = g_strdup("");
1381 g_array_insert_val(argv, 1, spacer);
1382 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1384 for (i = 1; i < g_strv_length(cmd); i++)
1385 g_array_prepend_val(argv, cmd[i]);
1387 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1388 TRUE, &uzbl.comm.sync_stdout);
1394 parse_command(const char *cmd, const char *param, GString *result) {
1397 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1399 gchar **par = split_quoted(param, TRUE);
1400 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1402 if (c->no_split) { /* don't split */
1403 sharg_append(a, param);
1405 for (i = 0; i < g_strv_length(par); i++)
1406 sharg_append(a, par[i]);
1409 if (result == NULL) {
1410 GString *result_print = g_string_new("");
1412 c->function(uzbl.gui.web_view, a, result_print);
1413 if (result_print->len)
1414 printf("%*s\n", result_print->len, result_print->str);
1416 g_string_free(result_print, TRUE);
1418 c->function(uzbl.gui.web_view, a, result);
1421 g_array_free (a, TRUE);
1424 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1431 if(*uzbl.net.proxy_url == ' '
1432 || uzbl.net.proxy_url == NULL) {
1433 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1434 (GType) SOUP_SESSION_PROXY_URI);
1437 suri = soup_uri_new(uzbl.net.proxy_url);
1438 g_object_set(G_OBJECT(uzbl.net.soup_session),
1439 SOUP_SESSION_PROXY_URI,
1441 soup_uri_free(suri);
1448 if(file_exists(uzbl.gui.icon)) {
1449 if (uzbl.gui.main_window)
1450 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1452 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1454 g_free (uzbl.gui.icon);
1459 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1460 g_array_append_val (a, uzbl.state.uri);
1461 load_uri(uzbl.gui.web_view, a, NULL);
1462 g_array_free (a, TRUE);
1466 cmd_always_insert_mode() {
1467 uzbl.behave.insert_mode =
1468 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1474 g_object_set(G_OBJECT(uzbl.net.soup_session),
1475 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1479 cmd_max_conns_host() {
1480 g_object_set(G_OBJECT(uzbl.net.soup_session),
1481 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1486 soup_session_remove_feature
1487 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1488 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1489 /*g_free(uzbl.net.soup_logger);*/
1491 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1492 soup_session_add_feature(uzbl.net.soup_session,
1493 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1496 static WebKitWebSettings*
1498 return webkit_web_view_get_settings(uzbl.gui.web_view);
1503 WebKitWebSettings *ws = view_settings();
1504 if (uzbl.behave.font_size > 0) {
1505 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1508 if (uzbl.behave.monospace_size > 0) {
1509 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1510 uzbl.behave.monospace_size, NULL);
1512 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1513 uzbl.behave.font_size, NULL);
1519 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1523 cmd_disable_plugins() {
1524 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1525 !uzbl.behave.disable_plugins, NULL);
1529 cmd_disable_scripts() {
1530 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1531 !uzbl.behave.disable_scripts, NULL);
1535 cmd_minimum_font_size() {
1536 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1537 uzbl.behave.minimum_font_size, NULL);
1540 cmd_autoload_img() {
1541 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1542 uzbl.behave.autoload_img, NULL);
1547 cmd_autoshrink_img() {
1548 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1549 uzbl.behave.autoshrink_img, NULL);
1554 cmd_enable_spellcheck() {
1555 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1556 uzbl.behave.enable_spellcheck, NULL);
1560 cmd_enable_private() {
1561 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1562 uzbl.behave.enable_private, NULL);
1567 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1568 uzbl.behave.print_bg, NULL);
1573 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1574 uzbl.behave.style_uri, NULL);
1578 cmd_resizable_txt() {
1579 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1580 uzbl.behave.resizable_txt, NULL);
1584 cmd_default_encoding() {
1585 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1586 uzbl.behave.default_encoding, NULL);
1590 cmd_enforce_96dpi() {
1591 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1592 uzbl.behave.enforce_96dpi, NULL);
1596 cmd_caret_browsing() {
1597 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1598 uzbl.behave.caret_browsing, NULL);
1602 cmd_cookie_handler() {
1603 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1604 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1605 if ((g_strcmp0(split[0], "sh") == 0) ||
1606 (g_strcmp0(split[0], "spawn") == 0)) {
1607 g_free (uzbl.behave.cookie_handler);
1608 uzbl.behave.cookie_handler =
1609 g_strdup_printf("sync_%s %s", split[0], split[1]);
1616 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1621 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1626 if(uzbl.behave.inject_html) {
1627 webkit_web_view_load_html_string (uzbl.gui.web_view,
1628 uzbl.behave.inject_html, NULL);
1637 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1638 uzbl.behave.modmask = 0;
1640 if(uzbl.behave.modkey)
1641 g_free(uzbl.behave.modkey);
1642 uzbl.behave.modkey = buf;
1644 for (i = 0; modkeys[i].key != NULL; i++) {
1645 if (g_strrstr(buf, modkeys[i].key))
1646 uzbl.behave.modmask |= modkeys[i].mask;
1652 if (*uzbl.net.useragent == ' ') {
1653 g_free (uzbl.net.useragent);
1654 uzbl.net.useragent = NULL;
1656 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1658 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1659 g_free(uzbl.net.useragent);
1660 uzbl.net.useragent = ua;
1666 gtk_widget_ref(uzbl.gui.scrolled_win);
1667 gtk_widget_ref(uzbl.gui.mainbar);
1668 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1669 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1671 if(uzbl.behave.status_top) {
1672 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1673 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1676 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.mainbar, FALSE, TRUE, 0);
1679 gtk_widget_unref(uzbl.gui.scrolled_win);
1680 gtk_widget_unref(uzbl.gui.mainbar);
1681 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1686 set_var_value(gchar *name, gchar *val) {
1687 uzbl_cmdprop *c = NULL;
1691 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1692 /* check for the variable type */
1693 if (c->type == TYPE_STR) {
1694 buf = expand_vars(val);
1697 } else if(c->type == TYPE_INT) {
1698 int *ip = (int *)c->ptr;
1699 buf = expand_vars(val);
1700 *ip = (int)strtoul(buf, &endp, 10);
1702 } else if (c->type == TYPE_FLOAT) {
1703 float *fp = (float *)c->ptr;
1704 buf = expand_vars(val);
1705 *fp = strtod(buf, &endp);
1709 /* invoke a command specific function */
1710 if(c->func) c->func();
1717 Behaviour *b = &uzbl.behave;
1719 if(b->html_buffer->str) {
1720 webkit_web_view_load_html_string (uzbl.gui.web_view,
1721 b->html_buffer->str, b->base_url);
1722 g_string_free(b->html_buffer, TRUE);
1723 b->html_buffer = g_string_new("");
1727 enum {M_CMD, M_HTML};
1729 parse_cmd_line(const char *ctl_line, GString *result) {
1730 Behaviour *b = &uzbl.behave;
1733 if(b->mode == M_HTML) {
1734 len = strlen(b->html_endmarker);
1735 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1736 if(len == strlen(ctl_line)-1 &&
1737 !strncmp(b->html_endmarker, ctl_line, len)) {
1739 set_var_value("mode", "0");
1744 set_timeout(b->html_timeout);
1745 g_string_append(b->html_buffer, ctl_line);
1748 else if((ctl_line[0] == '#') /* Comments */
1749 || (ctl_line[0] == ' ')
1750 || (ctl_line[0] == '\n'))
1751 ; /* ignore these lines */
1752 else { /* parse a command */
1754 gchar **tokens = NULL;
1755 len = strlen(ctl_line);
1757 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1758 ctlstrip = g_strndup(ctl_line, len - 1);
1759 else ctlstrip = g_strdup(ctl_line);
1761 tokens = g_strsplit(ctlstrip, " ", 2);
1762 parse_command(tokens[0], tokens[1], result);
1769 build_stream_name(int type, const gchar* dir) {
1770 char *xwin_str = NULL;
1771 State *s = &uzbl.state;
1774 xwin_str = itos((int)uzbl.xwin);
1776 str = g_strdup_printf
1777 ("%s/uzbl_fifo_%s", dir,
1778 s->instance_name ? s->instance_name : xwin_str);
1779 } else if (type == SOCKET) {
1780 str = g_strdup_printf
1781 ("%s/uzbl_socket_%s", dir,
1782 s->instance_name ? s->instance_name : xwin_str );
1789 control_fifo(GIOChannel *gio, GIOCondition condition) {
1790 if (uzbl.state.verbose)
1791 printf("triggered\n");
1796 if (condition & G_IO_HUP)
1797 g_error ("Fifo: Read end of pipe died!\n");
1800 g_error ("Fifo: GIOChannel broke\n");
1802 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1803 if (ret == G_IO_STATUS_ERROR) {
1804 g_error ("Fifo: Error reading: %s\n", err->message);
1808 parse_cmd_line(ctl_line, NULL);
1815 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1816 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1817 if (unlink(uzbl.comm.fifo_path) == -1)
1818 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1819 g_free(uzbl.comm.fifo_path);
1820 uzbl.comm.fifo_path = NULL;
1823 if (*dir == ' ') { /* space unsets the variable */
1828 GIOChannel *chan = NULL;
1829 GError *error = NULL;
1830 gchar *path = build_stream_name(FIFO, dir);
1832 if (!file_exists(path)) {
1833 if (mkfifo (path, 0666) == 0) {
1834 // 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.
1835 chan = g_io_channel_new_file(path, "r+", &error);
1837 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1838 if (uzbl.state.verbose)
1839 printf ("init_fifo: created successfully as %s\n", path);
1840 uzbl.comm.fifo_path = path;
1842 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1843 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1844 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1845 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1847 /* if we got this far, there was an error; cleanup */
1848 if (error) g_error_free (error);
1855 control_stdin(GIOChannel *gio, GIOCondition condition) {
1857 gchar *ctl_line = NULL;
1860 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1861 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1864 parse_cmd_line(ctl_line, NULL);
1872 GIOChannel *chan = NULL;
1873 GError *error = NULL;
1875 chan = g_io_channel_unix_new(fileno(stdin));
1877 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1878 g_error ("Stdin: could not add watch\n");
1880 if (uzbl.state.verbose)
1881 printf ("Stdin: watch added successfully\n");
1884 g_error ("Stdin: Error while opening: %s\n", error->message);
1886 if (error) g_error_free (error);
1890 control_socket(GIOChannel *chan) {
1891 struct sockaddr_un remote;
1892 unsigned int t = sizeof(remote);
1894 GIOChannel *clientchan;
1896 clientsock = accept (g_io_channel_unix_get_fd(chan),
1897 (struct sockaddr *) &remote, &t);
1899 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1900 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1901 (GIOFunc) control_client_socket, clientchan);
1908 control_client_socket(GIOChannel *clientchan) {
1910 GString *result = g_string_new("");
1911 GError *error = NULL;
1915 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1916 if (ret == G_IO_STATUS_ERROR) {
1917 g_warning ("Error reading: %s\n", error->message);
1918 g_io_channel_shutdown(clientchan, TRUE, &error);
1920 } else if (ret == G_IO_STATUS_EOF) {
1921 /* shutdown and remove channel watch from main loop */
1922 g_io_channel_shutdown(clientchan, TRUE, &error);
1927 parse_cmd_line (ctl_line, result);
1928 g_string_append_c(result, '\n');
1929 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1931 if (ret == G_IO_STATUS_ERROR) {
1932 g_warning ("Error writing: %s", error->message);
1934 g_io_channel_flush(clientchan, &error);
1937 if (error) g_error_free (error);
1938 g_string_free(result, TRUE);
1944 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1945 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1946 if (unlink(uzbl.comm.socket_path) == -1)
1947 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1948 g_free(uzbl.comm.socket_path);
1949 uzbl.comm.socket_path = NULL;
1957 GIOChannel *chan = NULL;
1959 struct sockaddr_un local;
1960 gchar *path = build_stream_name(SOCKET, dir);
1962 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1964 local.sun_family = AF_UNIX;
1965 strcpy (local.sun_path, path);
1966 unlink (local.sun_path);
1968 len = strlen (local.sun_path) + sizeof (local.sun_family);
1969 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1970 if (uzbl.state.verbose)
1971 printf ("init_socket: opened in %s\n", path);
1974 if( (chan = g_io_channel_unix_new(sock)) ) {
1975 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1976 uzbl.comm.socket_path = path;
1979 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1981 /* if we got this far, there was an error; cleanup */
1988 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1989 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1991 // this function may be called very early when the templates are not set (yet), hence the checks
1993 update_title (void) {
1994 Behaviour *b = &uzbl.behave;
1997 if (b->show_status) {
1998 if (b->title_format_short) {
1999 parsed = expand_template(b->title_format_short, FALSE);
2000 if (uzbl.gui.main_window)
2001 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2004 if (b->status_format) {
2005 parsed = expand_template(b->status_format, TRUE);
2006 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2009 if (b->status_background) {
2011 gdk_color_parse (b->status_background, &color);
2012 //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)
2013 if (uzbl.gui.main_window)
2014 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2017 if (b->title_format_long) {
2018 parsed = expand_template(b->title_format_long, FALSE);
2019 if (uzbl.gui.main_window)
2020 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2027 key_press_cb (GtkWidget* window, GdkEventKey* event)
2029 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2033 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2034 || 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)
2037 /* turn off insert mode (if always_insert_mode is not used) */
2038 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2039 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2044 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2047 if (event->keyval == GDK_Escape) {
2048 g_string_truncate(uzbl.state.keycmd, 0);
2050 dehilight(uzbl.gui.web_view, NULL, NULL);
2054 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2055 if (event->keyval == GDK_Insert) {
2057 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2058 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2060 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2063 g_string_append (uzbl.state.keycmd, str);
2070 if (event->keyval == GDK_BackSpace)
2071 keycmd_bs(NULL, NULL, NULL);
2073 gboolean key_ret = FALSE;
2074 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2076 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2078 run_keycmd(key_ret);
2080 if (key_ret) return (!uzbl.behave.insert_mode);
2085 run_keycmd(const gboolean key_ret) {
2086 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2088 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2089 g_string_truncate(uzbl.state.keycmd, 0);
2090 parse_command(act->name, act->param, NULL);
2094 /* try if it's an incremental keycmd or one that takes args, and run it */
2095 GString* short_keys = g_string_new ("");
2096 GString* short_keys_inc = g_string_new ("");
2098 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2099 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2100 g_string_assign(short_keys_inc, short_keys->str);
2101 g_string_append_c(short_keys, '_');
2102 g_string_append_c(short_keys_inc, '*');
2104 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2105 /* run normal cmds only if return was pressed */
2106 exec_paramcmd(act, i);
2107 g_string_truncate(uzbl.state.keycmd, 0);
2109 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2110 if (key_ret) /* just quit the incremental command on return */
2111 g_string_truncate(uzbl.state.keycmd, 0);
2112 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2116 g_string_truncate(short_keys, short_keys->len - 1);
2118 g_string_free (short_keys, TRUE);
2119 g_string_free (short_keys_inc, TRUE);
2123 exec_paramcmd(const Action *act, const guint i) {
2124 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2125 GString *actionname = g_string_new ("");
2126 GString *actionparam = g_string_new ("");
2127 g_string_erase (parampart, 0, i+1);
2129 g_string_printf (actionname, act->name, parampart->str);
2131 g_string_printf (actionparam, act->param, parampart->str);
2132 parse_command(actionname->str, actionparam->str, NULL);
2133 g_string_free(actionname, TRUE);
2134 g_string_free(actionparam, TRUE);
2135 g_string_free(parampart, TRUE);
2143 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2144 //main_window_ref = g_object_ref(scrolled_window);
2145 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
2147 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2148 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2150 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2151 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2152 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2153 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2154 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2155 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2156 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2157 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2158 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2159 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2160 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2162 return scrolled_window;
2169 g->mainbar = gtk_hbox_new (FALSE, 0);
2171 /* keep a reference to the bar so we can re-pack it at runtime*/
2172 //sbar_ref = g_object_ref(g->mainbar);
2174 g->mainbar_label = gtk_label_new ("");
2175 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2176 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2177 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2178 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2179 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2180 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2185 GtkWidget* create_window () {
2186 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2187 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2188 gtk_widget_set_name (window, "Uzbl browser");
2189 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2190 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2196 GtkPlug* create_plug () {
2197 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2198 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2199 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2206 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2208 If actname is one that calls an external command, this function will inject
2209 newargs in front of the user-provided args in that command line. They will
2210 come become after the body of the script (in sh) or after the name of
2211 the command to execute (in spawn).
2212 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2213 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2215 The return value consist of two strings: the action (sh, ...) and its args.
2217 If act is not one that calls an external command, then the given action merely
2220 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2221 gchar *actdup = g_strdup(actname);
2222 g_array_append_val(rets, actdup);
2224 if ((g_strcmp0(actname, "spawn") == 0) ||
2225 (g_strcmp0(actname, "sh") == 0) ||
2226 (g_strcmp0(actname, "sync_spawn") == 0) ||
2227 (g_strcmp0(actname, "sync_sh") == 0)) {
2229 GString *a = g_string_new("");
2230 gchar **spawnparts = split_quoted(origargs, FALSE);
2231 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2232 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2234 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2235 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2237 g_array_append_val(rets, a->str);
2238 g_string_free(a, FALSE);
2239 g_strfreev(spawnparts);
2241 gchar *origdup = g_strdup(origargs);
2242 g_array_append_val(rets, origdup);
2244 return (gchar**)g_array_free(rets, FALSE);
2248 run_handler (const gchar *act, const gchar *args) {
2249 /* Consider this code a temporary hack to make the handlers usable.
2250 In practice, all this splicing, injection, and reconstruction is
2251 inefficient, annoying and hard to manage. Potential pitfalls arise
2252 when the handler specific args 1) are not quoted (the handler
2253 callbacks should take care of this) 2) are quoted but interfere
2254 with the users' own quotation. A more ideal solution is
2255 to refactor parse_command so that it doesn't just take a string
2256 and execute it; rather than that, we should have a function which
2257 returns the argument vector parsed from the string. This vector
2258 could be modified (e.g. insert additional args into it) before
2259 passing it to the next function that actually executes it. Though
2260 it still isn't perfect for chain actions.. will reconsider & re-
2261 factor when I have the time. -duc */
2263 char **parts = g_strsplit(act, " ", 2);
2265 if (g_strcmp0(parts[0], "chain") == 0) {
2266 GString *newargs = g_string_new("");
2267 gchar **chainparts = split_quoted(parts[1], FALSE);
2269 /* for every argument in the chain, inject the handler args
2270 and make sure the new parts are wrapped in quotes */
2271 gchar **cp = chainparts;
2273 gchar *quotless = NULL;
2274 gchar **spliced_quotless = NULL; // sigh -_-;
2275 gchar **inpart = NULL;
2278 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2280 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2281 } else quotless = g_strdup(*cp);
2283 spliced_quotless = g_strsplit(quotless, " ", 2);
2284 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2285 g_strfreev(spliced_quotless);
2287 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2293 parse_command(parts[0], &(newargs->str[1]), NULL);
2294 g_string_free(newargs, TRUE);
2295 g_strfreev(chainparts);
2298 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2299 parse_command(inparts[0], inparts[1], NULL);
2307 add_binding (const gchar *key, const gchar *act) {
2308 char **parts = g_strsplit(act, " ", 2);
2315 if (uzbl.state.verbose)
2316 printf ("Binding %-10s : %s\n", key, act);
2317 action = new_action(parts[0], parts[1]);
2319 if (g_hash_table_remove (uzbl.bindings, key))
2320 g_warning ("Overwriting existing binding for \"%s\"", key);
2321 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2326 get_xdg_var (XDG_Var xdg) {
2327 const gchar* actual_value = getenv (xdg.environmental);
2328 const gchar* home = getenv ("HOME");
2329 gchar* return_value;
2331 if (! actual_value || strcmp (actual_value, "") == 0) {
2332 if (xdg.default_value) {
2333 return_value = str_replace ("~", home, xdg.default_value);
2335 return_value = NULL;
2338 return_value = str_replace("~", home, actual_value);
2341 return return_value;
2345 find_xdg_file (int xdg_type, char* filename) {
2346 /* xdg_type = 0 => config
2347 xdg_type = 1 => data
2348 xdg_type = 2 => cache*/
2350 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2351 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2354 gchar* temporary_string;
2358 if (! file_exists (temporary_file) && xdg_type != 2) {
2359 buf = get_xdg_var (XDG[3 + xdg_type]);
2360 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2363 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2364 g_free (temporary_file);
2365 temporary_file = g_strconcat (temporary_string, filename, NULL);
2369 //g_free (temporary_string); - segfaults.
2371 if (file_exists (temporary_file)) {
2372 return temporary_file;
2379 State *s = &uzbl.state;
2380 Network *n = &uzbl.net;
2382 for (i = 0; default_config[i].command != NULL; i++) {
2383 parse_cmd_line(default_config[i].command, NULL);
2386 if (!s->config_file) {
2387 s->config_file = find_xdg_file (0, "/uzbl/config");
2390 if (s->config_file) {
2391 GArray* lines = read_file_by_line (s->config_file);
2395 while ((line = g_array_index(lines, gchar*, i))) {
2396 parse_cmd_line (line, NULL);
2400 g_array_free (lines, TRUE);
2402 if (uzbl.state.verbose)
2403 printf ("No configuration file loaded.\n");
2406 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2409 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2412 if (!uzbl.behave.cookie_handler)
2415 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2416 GString *s = g_string_new ("");
2417 SoupURI * soup_uri = soup_message_get_uri(msg);
2418 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2419 run_handler(uzbl.behave.cookie_handler, s->str);
2421 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2422 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2423 if ( p != NULL ) *p = '\0';
2424 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2426 if (uzbl.comm.sync_stdout)
2427 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2429 g_string_free(s, TRUE);
2433 save_cookies (SoupMessage *msg, gpointer user_data){
2437 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2438 cookie = soup_cookie_to_set_cookie_header(ck->data);
2439 SoupURI * soup_uri = soup_message_get_uri(msg);
2440 GString *s = g_string_new ("");
2441 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2442 run_handler(uzbl.behave.cookie_handler, s->str);
2444 g_string_free(s, TRUE);
2449 /* --- WEBINSPECTOR --- */
2451 hide_window_cb(GtkWidget *widget, gpointer data) {
2454 gtk_widget_hide(widget);
2457 static WebKitWebView*
2458 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2461 (void) web_inspector;
2462 GtkWidget* scrolled_window;
2463 GtkWidget* new_web_view;
2466 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2467 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2468 G_CALLBACK(hide_window_cb), NULL);
2470 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2471 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2472 gtk_widget_show(g->inspector_window);
2474 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2475 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2476 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2477 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2478 gtk_widget_show(scrolled_window);
2480 new_web_view = webkit_web_view_new();
2481 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2483 return WEBKIT_WEB_VIEW(new_web_view);
2487 inspector_show_window_cb (WebKitWebInspector* inspector){
2489 gtk_widget_show(uzbl.gui.inspector_window);
2493 /* TODO: Add variables and code to make use of these functions */
2495 inspector_close_window_cb (WebKitWebInspector* inspector){
2501 inspector_attach_window_cb (WebKitWebInspector* inspector){
2507 inspector_detach_window_cb (WebKitWebInspector* inspector){
2513 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2519 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2525 set_up_inspector() {
2527 WebKitWebSettings *settings = view_settings();
2528 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2530 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2531 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2532 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2533 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2534 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2535 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2536 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2538 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2542 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2544 uzbl_cmdprop *c = v;
2549 if(c->type == TYPE_STR)
2550 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2551 else if(c->type == TYPE_INT)
2552 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2556 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2560 printf("bind %s = %s %s\n", (char *)k ,
2561 (char *)a->name, a->param?(char *)a->param:"");
2566 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2567 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2572 main (int argc, char* argv[]) {
2573 gtk_init (&argc, &argv);
2574 if (!g_thread_supported ())
2575 g_thread_init (NULL);
2576 uzbl.state.executable_path = g_strdup(argv[0]);
2577 uzbl.state.selected_url = NULL;
2578 uzbl.state.searchtx = NULL;
2580 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2581 g_option_context_add_main_entries (context, entries, NULL);
2582 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2583 g_option_context_parse (context, &argc, &argv, NULL);
2584 g_option_context_free(context);
2586 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2587 gboolean verbose_override = uzbl.state.verbose;
2589 /* initialize hash table */
2590 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2592 uzbl.net.soup_session = webkit_get_default_session();
2593 uzbl.state.keycmd = g_string_new("");
2595 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2596 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2597 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2598 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2599 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2600 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2603 if(uname(&uzbl.state.unameinfo) == -1)
2604 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2606 uzbl.gui.sbar.progress_s = g_strdup("=");
2607 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2608 uzbl.gui.sbar.progress_w = 10;
2610 /* HTML mode defaults*/
2611 uzbl.behave.html_buffer = g_string_new("");
2612 uzbl.behave.html_endmarker = g_strdup(".");
2613 uzbl.behave.html_timeout = 60;
2614 uzbl.behave.base_url = g_strdup("http://invalid");
2616 /* default mode indicators */
2617 uzbl.behave.insert_indicator = g_strdup("I");
2618 uzbl.behave.cmd_indicator = g_strdup("C");
2622 make_var_to_name_hash();
2624 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2626 uzbl.gui.scrolled_win = create_browser();
2629 /* initial packing */
2630 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2631 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2633 if (uzbl.state.socket_id) {
2634 uzbl.gui.plug = create_plug ();
2635 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2636 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2638 uzbl.gui.main_window = create_window ();
2639 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2640 gtk_widget_show_all (uzbl.gui.main_window);
2641 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2644 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2646 if (uzbl.state.verbose) {
2647 printf("Uzbl start location: %s\n", argv[0]);
2648 if (uzbl.state.socket_id)
2649 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2651 printf("window_id %i\n",(int) uzbl.xwin);
2652 printf("pid %i\n", getpid ());
2653 printf("name: %s\n", uzbl.state.instance_name);
2656 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2657 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2658 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2659 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2660 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2664 if (!uzbl.behave.show_status)
2665 gtk_widget_hide(uzbl.gui.mainbar);
2674 if (verbose_override > uzbl.state.verbose)
2675 uzbl.state.verbose = verbose_override;
2678 set_var_value("uri", uri_override);
2679 g_free(uri_override);
2680 } else if (uzbl.state.uri)
2681 cmd_load_uri(uzbl.gui.web_view, NULL);
2686 return EXIT_SUCCESS;
2689 /* vi: set et ts=4: */