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 gdouble page_size = gtk_adjustment_get_page_size(bar);
452 gdouble value = gtk_adjustment_get_value(bar);
453 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
456 value += page_size * amount * 0.01;
460 max_value = gtk_adjustment_get_upper(bar) - page_size;
462 if (value > max_value)
463 value = max_value; /* don't scroll past the end of the page */
465 gtk_adjustment_set_value (bar, value);
469 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
470 (void) page; (void) argv; (void) result;
471 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
475 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
476 (void) page; (void) argv; (void) result;
477 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
478 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
482 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
483 (void) page; (void) result;
484 scroll(uzbl.gui.bar_v, argv);
488 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
489 (void) page; (void) result;
490 scroll(uzbl.gui.bar_h, argv);
495 if (!uzbl.behave.show_status) {
496 gtk_widget_hide(uzbl.gui.mainbar);
498 gtk_widget_show(uzbl.gui.mainbar);
504 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
509 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
513 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
518 if (uzbl.behave.show_status) {
519 gtk_widget_hide(uzbl.gui.mainbar);
521 gtk_widget_show(uzbl.gui.mainbar);
523 uzbl.behave.show_status = !uzbl.behave.show_status;
528 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
532 //Set selected_url state variable
533 g_free(uzbl.state.selected_url);
534 uzbl.state.selected_url = NULL;
536 uzbl.state.selected_url = g_strdup(link);
542 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
546 if (uzbl.gui.main_title)
547 g_free (uzbl.gui.main_title);
548 uzbl.gui.main_title = g_strdup (title);
553 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
556 uzbl.gui.sbar.load_progress = progress;
561 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
565 if (uzbl.behave.load_finish_handler)
566 run_handler(uzbl.behave.load_finish_handler, "");
570 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
574 uzbl.gui.sbar.load_progress = 0;
575 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
576 if (uzbl.behave.load_start_handler)
577 run_handler(uzbl.behave.load_start_handler, "");
581 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
584 g_free (uzbl.state.uri);
585 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
586 uzbl.state.uri = g_string_free (newuri, FALSE);
587 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
588 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
591 if (uzbl.behave.load_commit_handler)
592 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
596 destroy_cb (GtkWidget* widget, gpointer data) {
604 if (uzbl.behave.history_handler) {
606 struct tm * timeinfo;
609 timeinfo = localtime ( &rawtime );
610 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
611 run_handler(uzbl.behave.history_handler, date);
616 /* VIEW funcs (little webkit wrappers) */
617 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
619 VIEWFUNC(reload_bypass_cache)
620 VIEWFUNC(stop_loading)
627 /* -- command to callback/function map for things we cannot attach to any signals */
628 static struct {char *key; CommandInfo value;} cmdlist[] =
629 { /* key function no_split */
630 { "back", {view_go_back, 0} },
631 { "forward", {view_go_forward, 0} },
632 { "scroll_vert", {scroll_vert, 0} },
633 { "scroll_horz", {scroll_horz, 0} },
634 { "scroll_begin", {scroll_begin, 0} },
635 { "scroll_end", {scroll_end, 0} },
636 { "reload", {view_reload, 0}, },
637 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
638 { "stop", {view_stop_loading, 0}, },
639 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
640 { "zoom_out", {view_zoom_out, 0}, },
641 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
642 { "uri", {load_uri, TRUE} },
643 { "js", {run_js, TRUE} },
644 { "script", {run_external_js, 0} },
645 { "toggle_status", {toggle_status_cb, 0} },
646 { "spawn", {spawn, 0} },
647 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
648 { "sh", {spawn_sh, 0} },
649 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
650 { "exit", {close_uzbl, 0} },
651 { "search", {search_forward_text, TRUE} },
652 { "search_reverse", {search_reverse_text, TRUE} },
653 { "dehilight", {dehilight, 0} },
654 { "toggle_insert_mode", {toggle_insert_mode, 0} },
655 { "set", {set_var, TRUE} },
656 //{ "get", {get_var, TRUE} },
657 { "bind", {act_bind, TRUE} },
658 { "dump_config", {act_dump_config, 0} },
659 { "keycmd", {keycmd, TRUE} },
660 { "keycmd_nl", {keycmd_nl, TRUE} },
661 { "keycmd_bs", {keycmd_bs, 0} },
662 { "chain", {chain, 0} },
663 { "print", {print, TRUE} }
670 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
672 for (i = 0; i < LENGTH(cmdlist); i++)
673 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
676 /* -- CORE FUNCTIONS -- */
679 free_action(gpointer act) {
680 Action *action = (Action*)act;
681 g_free(action->name);
683 g_free(action->param);
688 new_action(const gchar *name, const gchar *param) {
689 Action *action = g_new(Action, 1);
691 action->name = g_strdup(name);
693 action->param = g_strdup(param);
695 action->param = NULL;
701 file_exists (const char * filename) {
702 return (access(filename, F_OK) == 0);
706 set_var(WebKitWebView *page, GArray *argv, GString *result) {
707 (void) page; (void) result;
708 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
709 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
710 set_var_value(g_strstrip(split[0]), value);
716 print(WebKitWebView *page, GArray *argv, GString *result) {
717 (void) page; (void) result;
720 buf = expand_vars(argv_idx(argv, 0));
721 g_string_assign(result, buf);
726 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
727 (void) page; (void) result;
728 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
729 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
730 add_binding(g_strstrip(split[0]), value);
742 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
743 (void) page; (void) result;
745 if (argv_idx(argv, 0)) {
746 if (strcmp (argv_idx(argv, 0), "0") == 0) {
747 uzbl.behave.insert_mode = FALSE;
749 uzbl.behave.insert_mode = TRUE;
752 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
759 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
762 if (argv_idx(argv, 0)) {
763 GString* newuri = g_string_new (argv_idx(argv, 0));
764 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
765 run_js(web_view, argv, NULL);
768 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
769 g_string_prepend (newuri, "http://");
770 /* if we do handle cookies, ask our handler for them */
771 webkit_web_view_load_uri (web_view, newuri->str);
772 g_string_free (newuri, TRUE);
780 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
781 size_t argumentCount, const JSValueRef arguments[],
782 JSValueRef* exception) {
787 JSStringRef js_result_string;
788 GString *result = g_string_new("");
790 if (argumentCount >= 1) {
791 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
792 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
793 char ctl_line[arg_size];
794 JSStringGetUTF8CString(arg, ctl_line, arg_size);
796 parse_cmd_line(ctl_line, result);
798 JSStringRelease(arg);
800 js_result_string = JSStringCreateWithUTF8CString(result->str);
802 g_string_free(result, TRUE);
804 return JSValueMakeString(ctx, js_result_string);
807 static JSStaticFunction js_static_functions[] = {
808 {"run", js_run_command, kJSPropertyAttributeNone},
813 /* This function creates the class and its definition, only once */
814 if (!uzbl.js.initialized) {
815 /* it would be pretty cool to make this dynamic */
816 uzbl.js.classdef = kJSClassDefinitionEmpty;
817 uzbl.js.classdef.staticFunctions = js_static_functions;
819 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
825 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
826 WebKitWebFrame *frame;
827 JSGlobalContextRef context;
828 JSObjectRef globalobject;
829 JSStringRef var_name;
831 JSStringRef js_script;
832 JSValueRef js_result;
833 JSStringRef js_result_string;
834 size_t js_result_size;
838 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
839 context = webkit_web_frame_get_global_context(frame);
840 globalobject = JSContextGetGlobalObject(context);
842 /* uzbl javascript namespace */
843 var_name = JSStringCreateWithUTF8CString("Uzbl");
844 JSObjectSetProperty(context, globalobject, var_name,
845 JSObjectMake(context, uzbl.js.classref, NULL),
846 kJSClassAttributeNone, NULL);
848 /* evaluate the script and get return value*/
849 js_script = JSStringCreateWithUTF8CString(script);
850 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
851 if (js_result && !JSValueIsUndefined(context, js_result)) {
852 js_result_string = JSValueToStringCopy(context, js_result, NULL);
853 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
855 if (js_result_size) {
856 char js_result_utf8[js_result_size];
857 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
858 g_string_assign(result, js_result_utf8);
861 JSStringRelease(js_result_string);
865 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
867 JSStringRelease(var_name);
868 JSStringRelease(js_script);
872 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
874 if (argv_idx(argv, 0))
875 eval_js(web_view, argv_idx(argv, 0), result);
879 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
881 if (argv_idx(argv, 0)) {
882 GArray* lines = read_file_by_line (argv_idx (argv, 0));
887 while ((line = g_array_index(lines, gchar*, i))) {
889 js = g_strdup (line);
891 gchar* newjs = g_strconcat (js, line, NULL);
898 if (uzbl.state.verbose)
899 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
901 if (argv_idx (argv, 1)) {
902 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
906 eval_js (web_view, js, result);
908 g_array_free (lines, TRUE);
913 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
914 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
915 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
916 webkit_web_view_unmark_text_matches (page);
917 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
918 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
922 if (uzbl.state.searchtx) {
923 if (uzbl.state.verbose)
924 printf ("Searching: %s\n", uzbl.state.searchtx);
925 webkit_web_view_set_highlight_text_matches (page, TRUE);
926 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
931 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
933 search_text(page, argv, TRUE);
937 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
939 search_text(page, argv, FALSE);
943 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
944 (void) argv; (void) result;
945 webkit_web_view_set_highlight_text_matches (page, FALSE);
950 new_window_load_uri (const gchar * uri) {
951 GString* to_execute = g_string_new ("");
952 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
954 for (i = 0; entries[i].long_name != NULL; i++) {
955 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
956 gchar** str = (gchar**)entries[i].arg_data;
958 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
962 if (uzbl.state.verbose)
963 printf("\n%s\n", to_execute->str);
964 g_spawn_command_line_async (to_execute->str, NULL);
965 g_string_free (to_execute, TRUE);
969 chain (WebKitWebView *page, GArray *argv, GString *result) {
970 (void) page; (void) result;
972 gchar **parts = NULL;
974 while ((a = argv_idx(argv, i++))) {
975 parts = g_strsplit (a, " ", 2);
976 parse_command(parts[0], parts[1], result);
982 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
986 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
992 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
996 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1002 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1007 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1009 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1014 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1021 /* --Statusbar functions-- */
1023 build_progressbar_ascii(int percent) {
1024 int width=uzbl.gui.sbar.progress_w;
1027 GString *bar = g_string_new("");
1029 l = (double)percent*((double)width/100.);
1030 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1032 for(i=0; i<(int)l; i++)
1033 g_string_append(bar, uzbl.gui.sbar.progress_s);
1036 g_string_append(bar, uzbl.gui.sbar.progress_u);
1038 return g_string_free(bar, FALSE);
1043 const GScannerConfig scan_config = {
1046 ) /* cset_skip_characters */,
1051 ) /* cset_identifier_first */,
1058 ) /* cset_identifier_nth */,
1059 ( "" ) /* cpair_comment_single */,
1061 TRUE /* case_sensitive */,
1063 FALSE /* skip_comment_multi */,
1064 FALSE /* skip_comment_single */,
1065 FALSE /* scan_comment_multi */,
1066 TRUE /* scan_identifier */,
1067 TRUE /* scan_identifier_1char */,
1068 FALSE /* scan_identifier_NULL */,
1069 TRUE /* scan_symbols */,
1070 FALSE /* scan_binary */,
1071 FALSE /* scan_octal */,
1072 FALSE /* scan_float */,
1073 FALSE /* scan_hex */,
1074 FALSE /* scan_hex_dollar */,
1075 FALSE /* scan_string_sq */,
1076 FALSE /* scan_string_dq */,
1077 TRUE /* numbers_2_int */,
1078 FALSE /* int_2_float */,
1079 FALSE /* identifier_2_string */,
1080 FALSE /* char_2_token */,
1081 FALSE /* symbol_2_token */,
1082 TRUE /* scope_0_fallback */,
1087 uzbl.scan = g_scanner_new(&scan_config);
1088 while(symp->symbol_name) {
1089 g_scanner_scope_add_symbol(uzbl.scan, 0,
1091 GINT_TO_POINTER(symp->symbol_token));
1097 expand_template(const char *template, gboolean escape_markup) {
1098 if(!template) return NULL;
1100 GTokenType token = G_TOKEN_NONE;
1101 GString *ret = g_string_new("");
1105 g_scanner_input_text(uzbl.scan, template, strlen(template));
1106 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1107 token = g_scanner_get_next_token(uzbl.scan);
1109 if(token == G_TOKEN_SYMBOL) {
1110 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1114 buf = uzbl.state.uri?
1115 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1116 g_string_append(ret, buf);
1120 g_string_append(ret, uzbl.state.uri?
1121 uzbl.state.uri:g_strdup(""));
1124 buf = itos(uzbl.gui.sbar.load_progress);
1125 g_string_append(ret, buf);
1128 case SYM_LOADPRGSBAR:
1129 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1130 g_string_append(ret, buf);
1135 buf = uzbl.gui.main_title?
1136 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1137 g_string_append(ret, buf);
1141 g_string_append(ret, uzbl.gui.main_title?
1142 uzbl.gui.main_title:g_strdup(""));
1144 case SYM_SELECTED_URI:
1146 buf = uzbl.state.selected_url?
1147 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1148 g_string_append(ret, buf);
1152 g_string_append(ret, uzbl.state.selected_url?
1153 uzbl.state.selected_url:g_strdup(""));
1156 buf = itos(uzbl.xwin);
1157 g_string_append(ret,
1158 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1163 buf = uzbl.state.keycmd->str?
1164 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1165 g_string_append(ret, buf);
1169 g_string_append(ret, uzbl.state.keycmd->str?
1170 uzbl.state.keycmd->str:g_strdup(""));
1173 g_string_append(ret,
1174 uzbl.behave.insert_mode?
1175 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1178 g_string_append(ret,
1179 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1181 /* useragent syms */
1183 buf = itos(WEBKIT_MAJOR_VERSION);
1184 g_string_append(ret, buf);
1188 buf = itos(WEBKIT_MINOR_VERSION);
1189 g_string_append(ret, buf);
1193 buf = itos(WEBKIT_MICRO_VERSION);
1194 g_string_append(ret, buf);
1198 g_string_append(ret, uzbl.state.unameinfo.sysname);
1201 g_string_append(ret, uzbl.state.unameinfo.nodename);
1204 g_string_append(ret, uzbl.state.unameinfo.release);
1207 g_string_append(ret, uzbl.state.unameinfo.version);
1210 g_string_append(ret, uzbl.state.unameinfo.machine);
1213 g_string_append(ret, ARCH);
1216 case SYM_DOMAINNAME:
1217 g_string_append(ret, uzbl.state.unameinfo.domainname);
1221 g_string_append(ret, COMMIT);
1227 else if(token == G_TOKEN_INT) {
1228 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1229 g_string_append(ret, buf);
1232 else if(token == G_TOKEN_IDENTIFIER) {
1233 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1235 else if(token == G_TOKEN_CHAR) {
1236 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1240 return g_string_free(ret, FALSE);
1242 /* --End Statusbar functions-- */
1245 sharg_append(GArray *a, const gchar *str) {
1246 const gchar *s = (str ? str : "");
1247 g_array_append_val(a, s);
1250 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1252 run_command (const gchar *command, const guint npre, const gchar **args,
1253 const gboolean sync, char **output_stdout) {
1254 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1257 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1258 gchar *pid = itos(getpid());
1259 gchar *xwin = itos(uzbl.xwin);
1261 sharg_append(a, command);
1262 for (i = 0; i < npre; i++) /* add n args before the default vars */
1263 sharg_append(a, args[i]);
1264 sharg_append(a, uzbl.state.config_file);
1265 sharg_append(a, pid);
1266 sharg_append(a, xwin);
1267 sharg_append(a, uzbl.comm.fifo_path);
1268 sharg_append(a, uzbl.comm.socket_path);
1269 sharg_append(a, uzbl.state.uri);
1270 sharg_append(a, uzbl.gui.main_title);
1272 for (i = npre; i < g_strv_length((gchar**)args); i++)
1273 sharg_append(a, args[i]);
1277 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1279 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1280 NULL, NULL, output_stdout, NULL, NULL, &err);
1281 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1282 NULL, NULL, NULL, &err);
1284 if (uzbl.state.verbose) {
1285 GString *s = g_string_new("spawned:");
1286 for (i = 0; i < (a->len); i++) {
1287 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1288 g_string_append_printf(s, " %s", qarg);
1291 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1292 printf("%s\n", s->str);
1293 g_string_free(s, TRUE);
1295 printf("Stdout: %s\n", *output_stdout);
1299 g_printerr("error on run_command: %s\n", err->message);
1304 g_array_free (a, TRUE);
1309 split_quoted(const gchar* src, const gboolean unquote) {
1310 /* split on unquoted space, return array of strings;
1311 remove a layer of quotes and backslashes if unquote */
1312 if (!src) return NULL;
1314 gboolean dq = FALSE;
1315 gboolean sq = FALSE;
1316 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1317 GString *s = g_string_new ("");
1321 for (p = src; *p != '\0'; p++) {
1322 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1323 else if (*p == '\\') { g_string_append_c(s, *p++);
1324 g_string_append_c(s, *p); }
1325 else if ((*p == '"') && unquote && !sq) dq = !dq;
1326 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1328 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1329 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1331 else if ((*p == ' ') && !dq && !sq) {
1332 dup = g_strdup(s->str);
1333 g_array_append_val(a, dup);
1334 g_string_truncate(s, 0);
1335 } else g_string_append_c(s, *p);
1337 dup = g_strdup(s->str);
1338 g_array_append_val(a, dup);
1339 ret = (gchar**)a->data;
1340 g_array_free (a, FALSE);
1341 g_string_free (s, TRUE);
1346 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1347 (void)web_view; (void)result;
1348 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1349 if (argv_idx(argv, 0))
1350 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1354 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1355 (void)web_view; (void)result;
1357 if (argv_idx(argv, 0))
1358 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1359 TRUE, &uzbl.comm.sync_stdout);
1363 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1364 (void)web_view; (void)result;
1365 if (!uzbl.behave.shell_cmd) {
1366 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1371 gchar *spacer = g_strdup("");
1372 g_array_insert_val(argv, 1, spacer);
1373 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1375 for (i = 1; i < g_strv_length(cmd); i++)
1376 g_array_prepend_val(argv, cmd[i]);
1378 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1384 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1385 (void)web_view; (void)result;
1386 if (!uzbl.behave.shell_cmd) {
1387 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1392 gchar *spacer = g_strdup("");
1393 g_array_insert_val(argv, 1, spacer);
1394 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1396 for (i = 1; i < g_strv_length(cmd); i++)
1397 g_array_prepend_val(argv, cmd[i]);
1399 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1400 TRUE, &uzbl.comm.sync_stdout);
1406 parse_command(const char *cmd, const char *param, GString *result) {
1409 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1411 gchar **par = split_quoted(param, TRUE);
1412 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1414 if (c->no_split) { /* don't split */
1415 sharg_append(a, param);
1417 for (i = 0; i < g_strv_length(par); i++)
1418 sharg_append(a, par[i]);
1421 if (result == NULL) {
1422 GString *result_print = g_string_new("");
1424 c->function(uzbl.gui.web_view, a, result_print);
1425 if (result_print->len)
1426 printf("%*s\n", result_print->len, result_print->str);
1428 g_string_free(result_print, TRUE);
1430 c->function(uzbl.gui.web_view, a, result);
1433 g_array_free (a, TRUE);
1436 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1443 if(*uzbl.net.proxy_url == ' '
1444 || uzbl.net.proxy_url == NULL) {
1445 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1446 (GType) SOUP_SESSION_PROXY_URI);
1449 suri = soup_uri_new(uzbl.net.proxy_url);
1450 g_object_set(G_OBJECT(uzbl.net.soup_session),
1451 SOUP_SESSION_PROXY_URI,
1453 soup_uri_free(suri);
1460 if(file_exists(uzbl.gui.icon)) {
1461 if (uzbl.gui.main_window)
1462 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1464 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1466 g_free (uzbl.gui.icon);
1471 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1472 g_array_append_val (a, uzbl.state.uri);
1473 load_uri(uzbl.gui.web_view, a, NULL);
1474 g_array_free (a, TRUE);
1478 cmd_always_insert_mode() {
1479 uzbl.behave.insert_mode =
1480 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1486 g_object_set(G_OBJECT(uzbl.net.soup_session),
1487 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1491 cmd_max_conns_host() {
1492 g_object_set(G_OBJECT(uzbl.net.soup_session),
1493 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1498 soup_session_remove_feature
1499 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1500 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1501 /*g_free(uzbl.net.soup_logger);*/
1503 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1504 soup_session_add_feature(uzbl.net.soup_session,
1505 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1508 static WebKitWebSettings*
1510 return webkit_web_view_get_settings(uzbl.gui.web_view);
1515 WebKitWebSettings *ws = view_settings();
1516 if (uzbl.behave.font_size > 0) {
1517 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1520 if (uzbl.behave.monospace_size > 0) {
1521 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1522 uzbl.behave.monospace_size, NULL);
1524 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1525 uzbl.behave.font_size, NULL);
1531 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1535 cmd_disable_plugins() {
1536 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1537 !uzbl.behave.disable_plugins, NULL);
1541 cmd_disable_scripts() {
1542 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1543 !uzbl.behave.disable_scripts, NULL);
1547 cmd_minimum_font_size() {
1548 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1549 uzbl.behave.minimum_font_size, NULL);
1552 cmd_autoload_img() {
1553 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1554 uzbl.behave.autoload_img, NULL);
1559 cmd_autoshrink_img() {
1560 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1561 uzbl.behave.autoshrink_img, NULL);
1566 cmd_enable_spellcheck() {
1567 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1568 uzbl.behave.enable_spellcheck, NULL);
1572 cmd_enable_private() {
1573 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1574 uzbl.behave.enable_private, NULL);
1579 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1580 uzbl.behave.print_bg, NULL);
1585 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1586 uzbl.behave.style_uri, NULL);
1590 cmd_resizable_txt() {
1591 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1592 uzbl.behave.resizable_txt, NULL);
1596 cmd_default_encoding() {
1597 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1598 uzbl.behave.default_encoding, NULL);
1602 cmd_enforce_96dpi() {
1603 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1604 uzbl.behave.enforce_96dpi, NULL);
1608 cmd_caret_browsing() {
1609 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1610 uzbl.behave.caret_browsing, NULL);
1614 cmd_cookie_handler() {
1615 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1616 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1617 if ((g_strcmp0(split[0], "sh") == 0) ||
1618 (g_strcmp0(split[0], "spawn") == 0)) {
1619 g_free (uzbl.behave.cookie_handler);
1620 uzbl.behave.cookie_handler =
1621 g_strdup_printf("sync_%s %s", split[0], split[1]);
1628 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1633 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1638 if(uzbl.behave.inject_html) {
1639 webkit_web_view_load_html_string (uzbl.gui.web_view,
1640 uzbl.behave.inject_html, NULL);
1649 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1650 uzbl.behave.modmask = 0;
1652 if(uzbl.behave.modkey)
1653 g_free(uzbl.behave.modkey);
1654 uzbl.behave.modkey = buf;
1656 for (i = 0; modkeys[i].key != NULL; i++) {
1657 if (g_strrstr(buf, modkeys[i].key))
1658 uzbl.behave.modmask |= modkeys[i].mask;
1664 if (*uzbl.net.useragent == ' ') {
1665 g_free (uzbl.net.useragent);
1666 uzbl.net.useragent = NULL;
1668 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1670 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1671 g_free(uzbl.net.useragent);
1672 uzbl.net.useragent = ua;
1678 gtk_widget_ref(uzbl.gui.scrolled_win);
1679 gtk_widget_ref(uzbl.gui.mainbar);
1680 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1681 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1683 if(uzbl.behave.status_top) {
1684 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1685 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1688 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1689 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1691 gtk_widget_unref(uzbl.gui.scrolled_win);
1692 gtk_widget_unref(uzbl.gui.mainbar);
1693 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1698 set_var_value(gchar *name, gchar *val) {
1699 uzbl_cmdprop *c = NULL;
1703 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1704 /* check for the variable type */
1705 if (c->type == TYPE_STR) {
1706 buf = expand_vars(val);
1709 } else if(c->type == TYPE_INT) {
1710 int *ip = (int *)c->ptr;
1711 buf = expand_vars(val);
1712 *ip = (int)strtoul(buf, &endp, 10);
1714 } else if (c->type == TYPE_FLOAT) {
1715 float *fp = (float *)c->ptr;
1716 buf = expand_vars(val);
1717 *fp = strtod(buf, &endp);
1721 /* invoke a command specific function */
1722 if(c->func) c->func();
1729 Behaviour *b = &uzbl.behave;
1731 if(b->html_buffer->str) {
1732 webkit_web_view_load_html_string (uzbl.gui.web_view,
1733 b->html_buffer->str, b->base_url);
1734 g_string_free(b->html_buffer, TRUE);
1735 b->html_buffer = g_string_new("");
1739 enum {M_CMD, M_HTML};
1741 parse_cmd_line(const char *ctl_line, GString *result) {
1742 Behaviour *b = &uzbl.behave;
1745 if(b->mode == M_HTML) {
1746 len = strlen(b->html_endmarker);
1747 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1748 if(len == strlen(ctl_line)-1 &&
1749 !strncmp(b->html_endmarker, ctl_line, len)) {
1751 set_var_value("mode", "0");
1756 set_timeout(b->html_timeout);
1757 g_string_append(b->html_buffer, ctl_line);
1760 else if((ctl_line[0] == '#') /* Comments */
1761 || (ctl_line[0] == ' ')
1762 || (ctl_line[0] == '\n'))
1763 ; /* ignore these lines */
1764 else { /* parse a command */
1766 gchar **tokens = NULL;
1767 len = strlen(ctl_line);
1769 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1770 ctlstrip = g_strndup(ctl_line, len - 1);
1771 else ctlstrip = g_strdup(ctl_line);
1773 tokens = g_strsplit(ctlstrip, " ", 2);
1774 parse_command(tokens[0], tokens[1], result);
1781 build_stream_name(int type, const gchar* dir) {
1782 char *xwin_str = NULL;
1783 State *s = &uzbl.state;
1786 xwin_str = itos((int)uzbl.xwin);
1788 str = g_strdup_printf
1789 ("%s/uzbl_fifo_%s", dir,
1790 s->instance_name ? s->instance_name : xwin_str);
1791 } else if (type == SOCKET) {
1792 str = g_strdup_printf
1793 ("%s/uzbl_socket_%s", dir,
1794 s->instance_name ? s->instance_name : xwin_str );
1801 control_fifo(GIOChannel *gio, GIOCondition condition) {
1802 if (uzbl.state.verbose)
1803 printf("triggered\n");
1808 if (condition & G_IO_HUP)
1809 g_error ("Fifo: Read end of pipe died!\n");
1812 g_error ("Fifo: GIOChannel broke\n");
1814 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1815 if (ret == G_IO_STATUS_ERROR) {
1816 g_error ("Fifo: Error reading: %s\n", err->message);
1820 parse_cmd_line(ctl_line, NULL);
1827 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1828 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1829 if (unlink(uzbl.comm.fifo_path) == -1)
1830 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1831 g_free(uzbl.comm.fifo_path);
1832 uzbl.comm.fifo_path = NULL;
1835 if (*dir == ' ') { /* space unsets the variable */
1840 GIOChannel *chan = NULL;
1841 GError *error = NULL;
1842 gchar *path = build_stream_name(FIFO, dir);
1844 if (!file_exists(path)) {
1845 if (mkfifo (path, 0666) == 0) {
1846 // 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.
1847 chan = g_io_channel_new_file(path, "r+", &error);
1849 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1850 if (uzbl.state.verbose)
1851 printf ("init_fifo: created successfully as %s\n", path);
1852 uzbl.comm.fifo_path = path;
1854 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1855 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1856 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1857 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1859 /* if we got this far, there was an error; cleanup */
1860 if (error) g_error_free (error);
1867 control_stdin(GIOChannel *gio, GIOCondition condition) {
1869 gchar *ctl_line = NULL;
1872 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1873 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1876 parse_cmd_line(ctl_line, NULL);
1884 GIOChannel *chan = NULL;
1885 GError *error = NULL;
1887 chan = g_io_channel_unix_new(fileno(stdin));
1889 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1890 g_error ("Stdin: could not add watch\n");
1892 if (uzbl.state.verbose)
1893 printf ("Stdin: watch added successfully\n");
1896 g_error ("Stdin: Error while opening: %s\n", error->message);
1898 if (error) g_error_free (error);
1902 control_socket(GIOChannel *chan) {
1903 struct sockaddr_un remote;
1904 unsigned int t = sizeof(remote);
1906 GIOChannel *clientchan;
1908 clientsock = accept (g_io_channel_unix_get_fd(chan),
1909 (struct sockaddr *) &remote, &t);
1911 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1912 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1913 (GIOFunc) control_client_socket, clientchan);
1920 control_client_socket(GIOChannel *clientchan) {
1922 GString *result = g_string_new("");
1923 GError *error = NULL;
1927 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1928 if (ret == G_IO_STATUS_ERROR) {
1929 g_warning ("Error reading: %s\n", error->message);
1930 g_io_channel_shutdown(clientchan, TRUE, &error);
1932 } else if (ret == G_IO_STATUS_EOF) {
1933 /* shutdown and remove channel watch from main loop */
1934 g_io_channel_shutdown(clientchan, TRUE, &error);
1939 parse_cmd_line (ctl_line, result);
1940 g_string_append_c(result, '\n');
1941 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1943 if (ret == G_IO_STATUS_ERROR) {
1944 g_warning ("Error writing: %s", error->message);
1946 g_io_channel_flush(clientchan, &error);
1949 if (error) g_error_free (error);
1950 g_string_free(result, TRUE);
1956 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1957 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1958 if (unlink(uzbl.comm.socket_path) == -1)
1959 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1960 g_free(uzbl.comm.socket_path);
1961 uzbl.comm.socket_path = NULL;
1969 GIOChannel *chan = NULL;
1971 struct sockaddr_un local;
1972 gchar *path = build_stream_name(SOCKET, dir);
1974 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1976 local.sun_family = AF_UNIX;
1977 strcpy (local.sun_path, path);
1978 unlink (local.sun_path);
1980 len = strlen (local.sun_path) + sizeof (local.sun_family);
1981 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1982 if (uzbl.state.verbose)
1983 printf ("init_socket: opened in %s\n", path);
1986 if( (chan = g_io_channel_unix_new(sock)) ) {
1987 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1988 uzbl.comm.socket_path = path;
1991 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1993 /* if we got this far, there was an error; cleanup */
2000 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2001 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2003 // this function may be called very early when the templates are not set (yet), hence the checks
2005 update_title (void) {
2006 Behaviour *b = &uzbl.behave;
2009 if (b->show_status) {
2010 if (b->title_format_short) {
2011 parsed = expand_template(b->title_format_short, FALSE);
2012 if (uzbl.gui.main_window)
2013 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2016 if (b->status_format) {
2017 parsed = expand_template(b->status_format, TRUE);
2018 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2021 if (b->status_background) {
2023 gdk_color_parse (b->status_background, &color);
2024 //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)
2025 if (uzbl.gui.main_window)
2026 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2029 if (b->title_format_long) {
2030 parsed = expand_template(b->title_format_long, FALSE);
2031 if (uzbl.gui.main_window)
2032 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2039 key_press_cb (GtkWidget* window, GdkEventKey* event)
2041 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2045 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2046 || 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)
2049 /* turn off insert mode (if always_insert_mode is not used) */
2050 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2051 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2056 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2059 if (event->keyval == GDK_Escape) {
2060 g_string_truncate(uzbl.state.keycmd, 0);
2062 dehilight(uzbl.gui.web_view, NULL, NULL);
2066 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2067 if (event->keyval == GDK_Insert) {
2069 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2070 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2072 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2075 g_string_append (uzbl.state.keycmd, str);
2082 if (event->keyval == GDK_BackSpace)
2083 keycmd_bs(NULL, NULL, NULL);
2085 gboolean key_ret = FALSE;
2086 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2088 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2090 run_keycmd(key_ret);
2092 if (key_ret) return (!uzbl.behave.insert_mode);
2097 run_keycmd(const gboolean key_ret) {
2098 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2100 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2101 g_string_truncate(uzbl.state.keycmd, 0);
2102 parse_command(act->name, act->param, NULL);
2106 /* try if it's an incremental keycmd or one that takes args, and run it */
2107 GString* short_keys = g_string_new ("");
2108 GString* short_keys_inc = g_string_new ("");
2110 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2111 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2112 g_string_assign(short_keys_inc, short_keys->str);
2113 g_string_append_c(short_keys, '_');
2114 g_string_append_c(short_keys_inc, '*');
2116 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2117 /* run normal cmds only if return was pressed */
2118 exec_paramcmd(act, i);
2119 g_string_truncate(uzbl.state.keycmd, 0);
2121 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2122 if (key_ret) /* just quit the incremental command on return */
2123 g_string_truncate(uzbl.state.keycmd, 0);
2124 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2128 g_string_truncate(short_keys, short_keys->len - 1);
2130 g_string_free (short_keys, TRUE);
2131 g_string_free (short_keys_inc, TRUE);
2135 exec_paramcmd(const Action *act, const guint i) {
2136 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2137 GString *actionname = g_string_new ("");
2138 GString *actionparam = g_string_new ("");
2139 g_string_erase (parampart, 0, i+1);
2141 g_string_printf (actionname, act->name, parampart->str);
2143 g_string_printf (actionparam, act->param, parampart->str);
2144 parse_command(actionname->str, actionparam->str, NULL);
2145 g_string_free(actionname, TRUE);
2146 g_string_free(actionparam, TRUE);
2147 g_string_free(parampart, TRUE);
2155 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2156 //main_window_ref = g_object_ref(scrolled_window);
2157 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
2159 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2160 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2162 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2163 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2164 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2165 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2166 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2167 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2168 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2169 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2170 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2171 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2172 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2174 return scrolled_window;
2181 g->mainbar = gtk_hbox_new (FALSE, 0);
2183 /* keep a reference to the bar so we can re-pack it at runtime*/
2184 //sbar_ref = g_object_ref(g->mainbar);
2186 g->mainbar_label = gtk_label_new ("");
2187 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2188 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2189 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2190 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2191 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2192 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2197 GtkWidget* create_window () {
2198 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2199 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2200 gtk_widget_set_name (window, "Uzbl browser");
2201 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2202 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2208 GtkPlug* create_plug () {
2209 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2210 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2211 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2218 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2220 If actname is one that calls an external command, this function will inject
2221 newargs in front of the user-provided args in that command line. They will
2222 come become after the body of the script (in sh) or after the name of
2223 the command to execute (in spawn).
2224 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2225 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2227 The return value consist of two strings: the action (sh, ...) and its args.
2229 If act is not one that calls an external command, then the given action merely
2232 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2233 gchar *actdup = g_strdup(actname);
2234 g_array_append_val(rets, actdup);
2236 if ((g_strcmp0(actname, "spawn") == 0) ||
2237 (g_strcmp0(actname, "sh") == 0) ||
2238 (g_strcmp0(actname, "sync_spawn") == 0) ||
2239 (g_strcmp0(actname, "sync_sh") == 0)) {
2241 GString *a = g_string_new("");
2242 gchar **spawnparts = split_quoted(origargs, FALSE);
2243 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2244 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2246 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2247 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2249 g_array_append_val(rets, a->str);
2250 g_string_free(a, FALSE);
2251 g_strfreev(spawnparts);
2253 gchar *origdup = g_strdup(origargs);
2254 g_array_append_val(rets, origdup);
2256 return (gchar**)g_array_free(rets, FALSE);
2260 run_handler (const gchar *act, const gchar *args) {
2261 /* Consider this code a temporary hack to make the handlers usable.
2262 In practice, all this splicing, injection, and reconstruction is
2263 inefficient, annoying and hard to manage. Potential pitfalls arise
2264 when the handler specific args 1) are not quoted (the handler
2265 callbacks should take care of this) 2) are quoted but interfere
2266 with the users' own quotation. A more ideal solution is
2267 to refactor parse_command so that it doesn't just take a string
2268 and execute it; rather than that, we should have a function which
2269 returns the argument vector parsed from the string. This vector
2270 could be modified (e.g. insert additional args into it) before
2271 passing it to the next function that actually executes it. Though
2272 it still isn't perfect for chain actions.. will reconsider & re-
2273 factor when I have the time. -duc */
2275 char **parts = g_strsplit(act, " ", 2);
2277 if (g_strcmp0(parts[0], "chain") == 0) {
2278 GString *newargs = g_string_new("");
2279 gchar **chainparts = split_quoted(parts[1], FALSE);
2281 /* for every argument in the chain, inject the handler args
2282 and make sure the new parts are wrapped in quotes */
2283 gchar **cp = chainparts;
2285 gchar *quotless = NULL;
2286 gchar **spliced_quotless = NULL; // sigh -_-;
2287 gchar **inpart = NULL;
2290 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2292 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2293 } else quotless = g_strdup(*cp);
2295 spliced_quotless = g_strsplit(quotless, " ", 2);
2296 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2297 g_strfreev(spliced_quotless);
2299 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2305 parse_command(parts[0], &(newargs->str[1]), NULL);
2306 g_string_free(newargs, TRUE);
2307 g_strfreev(chainparts);
2310 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2311 parse_command(inparts[0], inparts[1], NULL);
2319 add_binding (const gchar *key, const gchar *act) {
2320 char **parts = g_strsplit(act, " ", 2);
2327 if (uzbl.state.verbose)
2328 printf ("Binding %-10s : %s\n", key, act);
2329 action = new_action(parts[0], parts[1]);
2331 if (g_hash_table_remove (uzbl.bindings, key))
2332 g_warning ("Overwriting existing binding for \"%s\"", key);
2333 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2338 get_xdg_var (XDG_Var xdg) {
2339 const gchar* actual_value = getenv (xdg.environmental);
2340 const gchar* home = getenv ("HOME");
2341 gchar* return_value;
2343 if (! actual_value || strcmp (actual_value, "") == 0) {
2344 if (xdg.default_value) {
2345 return_value = str_replace ("~", home, xdg.default_value);
2347 return_value = NULL;
2350 return_value = str_replace("~", home, actual_value);
2353 return return_value;
2357 find_xdg_file (int xdg_type, char* filename) {
2358 /* xdg_type = 0 => config
2359 xdg_type = 1 => data
2360 xdg_type = 2 => cache*/
2362 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2363 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2366 gchar* temporary_string;
2370 if (! file_exists (temporary_file) && xdg_type != 2) {
2371 buf = get_xdg_var (XDG[3 + xdg_type]);
2372 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2375 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2376 g_free (temporary_file);
2377 temporary_file = g_strconcat (temporary_string, filename, NULL);
2381 //g_free (temporary_string); - segfaults.
2383 if (file_exists (temporary_file)) {
2384 return temporary_file;
2391 State *s = &uzbl.state;
2392 Network *n = &uzbl.net;
2394 for (i = 0; default_config[i].command != NULL; i++) {
2395 parse_cmd_line(default_config[i].command, NULL);
2398 if (!s->config_file) {
2399 s->config_file = find_xdg_file (0, "/uzbl/config");
2402 if (s->config_file) {
2403 GArray* lines = read_file_by_line (s->config_file);
2407 while ((line = g_array_index(lines, gchar*, i))) {
2408 parse_cmd_line (line, NULL);
2412 g_array_free (lines, TRUE);
2414 if (uzbl.state.verbose)
2415 printf ("No configuration file loaded.\n");
2418 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2421 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2424 if (!uzbl.behave.cookie_handler)
2427 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2428 GString *s = g_string_new ("");
2429 SoupURI * soup_uri = soup_message_get_uri(msg);
2430 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2431 run_handler(uzbl.behave.cookie_handler, s->str);
2433 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2434 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2435 if ( p != NULL ) *p = '\0';
2436 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2438 if (uzbl.comm.sync_stdout)
2439 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2441 g_string_free(s, TRUE);
2445 save_cookies (SoupMessage *msg, gpointer user_data){
2449 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2450 cookie = soup_cookie_to_set_cookie_header(ck->data);
2451 SoupURI * soup_uri = soup_message_get_uri(msg);
2452 GString *s = g_string_new ("");
2453 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2454 run_handler(uzbl.behave.cookie_handler, s->str);
2456 g_string_free(s, TRUE);
2461 /* --- WEBINSPECTOR --- */
2463 hide_window_cb(GtkWidget *widget, gpointer data) {
2466 gtk_widget_hide(widget);
2469 static WebKitWebView*
2470 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2473 (void) web_inspector;
2474 GtkWidget* scrolled_window;
2475 GtkWidget* new_web_view;
2478 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2479 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2480 G_CALLBACK(hide_window_cb), NULL);
2482 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2483 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2484 gtk_widget_show(g->inspector_window);
2486 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2487 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2488 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2489 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2490 gtk_widget_show(scrolled_window);
2492 new_web_view = webkit_web_view_new();
2493 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2495 return WEBKIT_WEB_VIEW(new_web_view);
2499 inspector_show_window_cb (WebKitWebInspector* inspector){
2501 gtk_widget_show(uzbl.gui.inspector_window);
2505 /* TODO: Add variables and code to make use of these functions */
2507 inspector_close_window_cb (WebKitWebInspector* inspector){
2513 inspector_attach_window_cb (WebKitWebInspector* inspector){
2519 inspector_detach_window_cb (WebKitWebInspector* inspector){
2525 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2531 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2537 set_up_inspector() {
2539 WebKitWebSettings *settings = view_settings();
2540 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2542 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2543 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2544 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2545 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2546 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2547 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2548 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2550 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2554 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2556 uzbl_cmdprop *c = v;
2561 if(c->type == TYPE_STR)
2562 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2563 else if(c->type == TYPE_INT)
2564 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2568 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2572 printf("bind %s = %s %s\n", (char *)k ,
2573 (char *)a->name, a->param?(char *)a->param:"");
2578 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2579 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2584 main (int argc, char* argv[]) {
2585 gtk_init (&argc, &argv);
2586 if (!g_thread_supported ())
2587 g_thread_init (NULL);
2588 uzbl.state.executable_path = g_strdup(argv[0]);
2589 uzbl.state.selected_url = NULL;
2590 uzbl.state.searchtx = NULL;
2592 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2593 g_option_context_add_main_entries (context, entries, NULL);
2594 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2595 g_option_context_parse (context, &argc, &argv, NULL);
2596 g_option_context_free(context);
2598 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2599 gboolean verbose_override = uzbl.state.verbose;
2601 /* initialize hash table */
2602 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2604 uzbl.net.soup_session = webkit_get_default_session();
2605 uzbl.state.keycmd = g_string_new("");
2607 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2608 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2609 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2610 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2611 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2612 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2615 if(uname(&uzbl.state.unameinfo) == -1)
2616 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2618 uzbl.gui.sbar.progress_s = g_strdup("=");
2619 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2620 uzbl.gui.sbar.progress_w = 10;
2622 /* HTML mode defaults*/
2623 uzbl.behave.html_buffer = g_string_new("");
2624 uzbl.behave.html_endmarker = g_strdup(".");
2625 uzbl.behave.html_timeout = 60;
2626 uzbl.behave.base_url = g_strdup("http://invalid");
2628 /* default mode indicators */
2629 uzbl.behave.insert_indicator = g_strdup("I");
2630 uzbl.behave.cmd_indicator = g_strdup("C");
2634 make_var_to_name_hash();
2636 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2638 uzbl.gui.scrolled_win = create_browser();
2641 /* initial packing */
2642 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2643 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2645 if (uzbl.state.socket_id) {
2646 uzbl.gui.plug = create_plug ();
2647 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2648 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2650 uzbl.gui.main_window = create_window ();
2651 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2652 gtk_widget_show_all (uzbl.gui.main_window);
2653 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2656 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2658 if (uzbl.state.verbose) {
2659 printf("Uzbl start location: %s\n", argv[0]);
2660 if (uzbl.state.socket_id)
2661 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2663 printf("window_id %i\n",(int) uzbl.xwin);
2664 printf("pid %i\n", getpid ());
2665 printf("name: %s\n", uzbl.state.instance_name);
2668 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2669 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2670 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2671 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2672 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2676 if (!uzbl.behave.show_status)
2677 gtk_widget_hide(uzbl.gui.mainbar);
2686 if (verbose_override > uzbl.state.verbose)
2687 uzbl.state.verbose = verbose_override;
2690 set_var_value("uri", uri_override);
2691 g_free(uri_override);
2692 } else if (uzbl.state.uri)
2693 cmd_load_uri(uzbl.gui.web_view, NULL);
2698 return EXIT_SUCCESS;
2701 /* vi: set et ts=4: */