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 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
197 get_exp_type(gchar *s) {
201 else if(*(s+1) == '{')
202 return EXP_BRACED_VAR;
203 else if(*(s+1) == '<')
206 return EXP_SIMPLE_VAR;
211 /* setting 'recurse = 1' will prevent expand() from
212 * expanding '@(command)'
215 expand(char *s, gboolean recurse) {
222 gchar *cmd_stdout = NULL;
224 GString *buf = g_string_new("");
225 GString *js_ret = g_string_new("");
230 g_string_append_c(buf, *++s);
235 etype = get_exp_type(s);
253 if( (vend = strchr(s, upto)) ||
254 (vend = strchr(s, '\0')) ) {
255 strncpy(ret, s, vend-s);
259 if(etype == EXP_SIMPLE_VAR ||
260 etype == EXP_BRACED_VAR) {
261 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
262 if(c->type == TYPE_STR)
263 g_string_append(buf, (gchar *)*c->ptr);
264 else if(c->type == TYPE_INT) {
265 char *b = itos((int)*c->ptr);
266 g_string_append(buf, b);
270 if(upto == ' ') s = vend;
275 mycmd = expand(ret, 1);
276 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
280 g_printerr("error on running command: %s\n", err->message);
283 else if (*cmd_stdout) {
284 g_string_append(buf, cmd_stdout);
291 mycmd = expand(ret, 1);
292 eval_js(uzbl.gui.web_view, mycmd, js_ret);
296 g_string_append(buf, js_ret->str);
297 g_string_free(js_ret, 1);
303 g_string_append_c(buf, *s);
308 return g_string_free(buf, FALSE);
315 snprintf(tmp, sizeof(tmp), "%i", val);
316 return g_strdup(tmp);
320 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
323 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
326 str_replace (const char* search, const char* replace, const char* string) {
330 buf = g_strsplit (string, search, -1);
331 ret = g_strjoinv (replace, buf);
332 g_strfreev(buf); // somebody said this segfaults
338 read_file_by_line (gchar *path) {
339 GIOChannel *chan = NULL;
340 gchar *readbuf = NULL;
342 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
345 chan = g_io_channel_new_file(path, "r", NULL);
348 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
349 const gchar* val = g_strdup (readbuf);
350 g_array_append_val (lines, val);
355 g_io_channel_unref (chan);
357 fprintf(stderr, "File '%s' not be read.\n", path);
364 gchar* parseenv (char* string) {
365 extern char** environ;
366 gchar* tmpstr = NULL;
370 while (environ[i] != NULL) {
371 gchar** env = g_strsplit (environ[i], "=", 2);
372 gchar* envname = g_strconcat ("$", env[0], NULL);
374 if (g_strrstr (string, envname) != NULL) {
375 tmpstr = g_strdup(string);
377 string = str_replace(envname, env[1], tmpstr);
382 g_strfreev (env); // somebody said this breaks uzbl
390 setup_signal(int signr, sigfunc *shandler) {
391 struct sigaction nh, oh;
393 nh.sa_handler = shandler;
394 sigemptyset(&nh.sa_mask);
397 if(sigaction(signr, &nh, &oh) < 0)
405 if (uzbl.behave.fifo_dir)
406 unlink (uzbl.comm.fifo_path);
407 if (uzbl.behave.socket_dir)
408 unlink (uzbl.comm.socket_path);
410 g_free(uzbl.state.executable_path);
411 g_string_free(uzbl.state.keycmd, TRUE);
412 g_hash_table_destroy(uzbl.bindings);
413 g_hash_table_destroy(uzbl.behave.commands);
416 /* used for html_mode_timeout
417 * be sure to extend this function to use
418 * more timers if needed in other places
421 set_timeout(int seconds) {
423 memset(&t, 0, sizeof t);
425 t.it_value.tv_sec = seconds;
426 t.it_value.tv_usec = 0;
427 setitimer(ITIMER_REAL, &t, NULL);
430 /* --- SIGNAL HANDLER --- */
433 catch_sigterm(int s) {
439 catch_sigint(int s) {
449 set_var_value("mode", "0");
454 /* --- CALLBACKS --- */
457 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
460 (void) navigation_action;
461 (void) policy_decision;
463 const gchar* uri = webkit_network_request_get_uri (request);
464 if (uzbl.state.verbose)
465 printf("New window requested -> %s \n", uri);
466 new_window_load_uri(uri);
471 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
476 /* If we can display it, let's display it... */
477 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
478 webkit_web_policy_decision_use (policy_decision);
482 /* ...everything we can't displayed is downloaded */
483 webkit_web_policy_decision_download (policy_decision);
488 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
492 if (uzbl.state.selected_url != NULL) {
493 if (uzbl.state.verbose)
494 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
495 new_window_load_uri(uzbl.state.selected_url);
497 if (uzbl.state.verbose)
498 printf("New web view -> %s\n","Nothing to open, exiting");
504 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
507 if (uzbl.behave.download_handler) {
508 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
509 if (uzbl.state.verbose)
510 printf("Download -> %s\n",uri);
511 /* if urls not escaped, we may have to escape and quote uri before this call */
512 run_handler(uzbl.behave.download_handler, uri);
517 /* scroll a bar in a given direction */
519 scroll (GtkAdjustment* bar, GArray *argv) {
523 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
524 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
525 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
529 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
530 (void) page; (void) argv; (void) result;
531 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
535 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
536 (void) page; (void) argv; (void) result;
537 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
538 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
542 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
543 (void) page; (void) result;
544 scroll(uzbl.gui.bar_v, argv);
548 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
549 (void) page; (void) result;
550 scroll(uzbl.gui.bar_h, argv);
555 if (!uzbl.behave.show_status) {
556 gtk_widget_hide(uzbl.gui.mainbar);
558 gtk_widget_show(uzbl.gui.mainbar);
564 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
569 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
573 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
578 if (uzbl.behave.show_status) {
579 gtk_widget_hide(uzbl.gui.mainbar);
581 gtk_widget_show(uzbl.gui.mainbar);
583 uzbl.behave.show_status = !uzbl.behave.show_status;
588 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
592 //Set selected_url state variable
593 g_free(uzbl.state.selected_url);
594 uzbl.state.selected_url = NULL;
596 uzbl.state.selected_url = g_strdup(link);
602 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
606 if (uzbl.gui.main_title)
607 g_free (uzbl.gui.main_title);
608 uzbl.gui.main_title = g_strdup (title);
613 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
616 uzbl.gui.sbar.load_progress = progress;
621 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
625 if (uzbl.behave.load_finish_handler)
626 run_handler(uzbl.behave.load_finish_handler, "");
630 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
634 uzbl.gui.sbar.load_progress = 0;
635 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
636 if (uzbl.behave.load_start_handler)
637 run_handler(uzbl.behave.load_start_handler, "");
641 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
644 g_free (uzbl.state.uri);
645 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
646 uzbl.state.uri = g_string_free (newuri, FALSE);
647 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
648 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
651 if (uzbl.behave.load_commit_handler)
652 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
656 destroy_cb (GtkWidget* widget, gpointer data) {
664 if (uzbl.behave.history_handler) {
666 struct tm * timeinfo;
669 timeinfo = localtime ( &rawtime );
670 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
671 run_handler(uzbl.behave.history_handler, date);
676 /* VIEW funcs (little webkit wrappers) */
677 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
679 VIEWFUNC(reload_bypass_cache)
680 VIEWFUNC(stop_loading)
687 /* -- command to callback/function map for things we cannot attach to any signals */
688 static struct {char *key; CommandInfo value;} cmdlist[] =
689 { /* key function no_split */
690 { "back", {view_go_back, 0} },
691 { "forward", {view_go_forward, 0} },
692 { "scroll_vert", {scroll_vert, 0} },
693 { "scroll_horz", {scroll_horz, 0} },
694 { "scroll_begin", {scroll_begin, 0} },
695 { "scroll_end", {scroll_end, 0} },
696 { "reload", {view_reload, 0}, },
697 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
698 { "stop", {view_stop_loading, 0}, },
699 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
700 { "zoom_out", {view_zoom_out, 0}, },
701 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
702 { "uri", {load_uri, TRUE} },
703 { "js", {run_js, TRUE} },
704 { "script", {run_external_js, 0} },
705 { "toggle_status", {toggle_status_cb, 0} },
706 { "spawn", {spawn, 0} },
707 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
708 { "sh", {spawn_sh, 0} },
709 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
710 { "exit", {close_uzbl, 0} },
711 { "search", {search_forward_text, TRUE} },
712 { "search_reverse", {search_reverse_text, TRUE} },
713 { "dehilight", {dehilight, 0} },
714 { "toggle_insert_mode", {toggle_insert_mode, 0} },
715 { "set", {set_var, TRUE} },
716 //{ "get", {get_var, TRUE} },
717 { "bind", {act_bind, TRUE} },
718 { "dump_config", {act_dump_config, 0} },
719 { "keycmd", {keycmd, TRUE} },
720 { "keycmd_nl", {keycmd_nl, TRUE} },
721 { "keycmd_bs", {keycmd_bs, 0} },
722 { "chain", {chain, 0} },
723 { "print", {print, TRUE} }
730 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
732 for (i = 0; i < LENGTH(cmdlist); i++)
733 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
736 /* -- CORE FUNCTIONS -- */
739 free_action(gpointer act) {
740 Action *action = (Action*)act;
741 g_free(action->name);
743 g_free(action->param);
748 new_action(const gchar *name, const gchar *param) {
749 Action *action = g_new(Action, 1);
751 action->name = g_strdup(name);
753 action->param = g_strdup(param);
755 action->param = NULL;
761 file_exists (const char * filename) {
762 return (access(filename, F_OK) == 0);
766 set_var(WebKitWebView *page, GArray *argv, GString *result) {
767 (void) page; (void) result;
768 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
769 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
770 set_var_value(g_strstrip(split[0]), value);
776 print(WebKitWebView *page, GArray *argv, GString *result) {
777 (void) page; (void) result;
780 buf = expand(argv_idx(argv, 0), 0);
781 g_string_assign(result, buf);
786 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
787 (void) page; (void) result;
788 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
789 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
790 add_binding(g_strstrip(split[0]), value);
802 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
803 (void) page; (void) result;
805 if (argv_idx(argv, 0)) {
806 if (strcmp (argv_idx(argv, 0), "0") == 0) {
807 uzbl.behave.insert_mode = FALSE;
809 uzbl.behave.insert_mode = TRUE;
812 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
819 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
822 if (argv_idx(argv, 0)) {
823 GString* newuri = g_string_new (argv_idx(argv, 0));
824 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
825 run_js(web_view, argv, NULL);
828 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
829 g_string_prepend (newuri, "http://");
830 /* if we do handle cookies, ask our handler for them */
831 webkit_web_view_load_uri (web_view, newuri->str);
832 g_string_free (newuri, TRUE);
840 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
841 size_t argumentCount, const JSValueRef arguments[],
842 JSValueRef* exception) {
847 JSStringRef js_result_string;
848 GString *result = g_string_new("");
850 if (argumentCount >= 1) {
851 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
852 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
853 char ctl_line[arg_size];
854 JSStringGetUTF8CString(arg, ctl_line, arg_size);
856 parse_cmd_line(ctl_line, result);
858 JSStringRelease(arg);
860 js_result_string = JSStringCreateWithUTF8CString(result->str);
862 g_string_free(result, TRUE);
864 return JSValueMakeString(ctx, js_result_string);
867 static JSStaticFunction js_static_functions[] = {
868 {"run", js_run_command, kJSPropertyAttributeNone},
873 /* This function creates the class and its definition, only once */
874 if (!uzbl.js.initialized) {
875 /* it would be pretty cool to make this dynamic */
876 uzbl.js.classdef = kJSClassDefinitionEmpty;
877 uzbl.js.classdef.staticFunctions = js_static_functions;
879 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
885 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
886 WebKitWebFrame *frame;
887 JSGlobalContextRef context;
888 JSObjectRef globalobject;
889 JSStringRef var_name;
891 JSStringRef js_script;
892 JSValueRef js_result;
893 JSStringRef js_result_string;
894 size_t js_result_size;
898 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
899 context = webkit_web_frame_get_global_context(frame);
900 globalobject = JSContextGetGlobalObject(context);
902 /* uzbl javascript namespace */
903 var_name = JSStringCreateWithUTF8CString("Uzbl");
904 JSObjectSetProperty(context, globalobject, var_name,
905 JSObjectMake(context, uzbl.js.classref, NULL),
906 kJSClassAttributeNone, NULL);
908 /* evaluate the script and get return value*/
909 js_script = JSStringCreateWithUTF8CString(script);
910 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
911 if (js_result && !JSValueIsUndefined(context, js_result)) {
912 js_result_string = JSValueToStringCopy(context, js_result, NULL);
913 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
915 if (js_result_size) {
916 char js_result_utf8[js_result_size];
917 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
918 g_string_assign(result, js_result_utf8);
921 JSStringRelease(js_result_string);
925 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
927 JSStringRelease(var_name);
928 JSStringRelease(js_script);
932 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
934 if (argv_idx(argv, 0))
935 eval_js(web_view, argv_idx(argv, 0), result);
939 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
941 if (argv_idx(argv, 0)) {
942 GArray* lines = read_file_by_line (argv_idx (argv, 0));
947 while ((line = g_array_index(lines, gchar*, i))) {
949 js = g_strdup (line);
951 gchar* newjs = g_strconcat (js, line, NULL);
958 if (uzbl.state.verbose)
959 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
961 if (argv_idx (argv, 1)) {
962 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
966 eval_js (web_view, js, result);
968 g_array_free (lines, TRUE);
973 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
974 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
975 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
976 webkit_web_view_unmark_text_matches (page);
977 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
978 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
982 if (uzbl.state.searchtx) {
983 if (uzbl.state.verbose)
984 printf ("Searching: %s\n", uzbl.state.searchtx);
985 webkit_web_view_set_highlight_text_matches (page, TRUE);
986 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
991 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
993 search_text(page, argv, TRUE);
997 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
999 search_text(page, argv, FALSE);
1003 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1004 (void) argv; (void) result;
1005 webkit_web_view_set_highlight_text_matches (page, FALSE);
1010 new_window_load_uri (const gchar * uri) {
1011 GString* to_execute = g_string_new ("");
1012 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1014 for (i = 0; entries[i].long_name != NULL; i++) {
1015 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1016 gchar** str = (gchar**)entries[i].arg_data;
1018 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1022 if (uzbl.state.verbose)
1023 printf("\n%s\n", to_execute->str);
1024 g_spawn_command_line_async (to_execute->str, NULL);
1025 g_string_free (to_execute, TRUE);
1029 chain (WebKitWebView *page, GArray *argv, GString *result) {
1030 (void) page; (void) result;
1032 gchar **parts = NULL;
1034 while ((a = argv_idx(argv, i++))) {
1035 parts = g_strsplit (a, " ", 2);
1036 parse_command(parts[0], parts[1], result);
1042 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1046 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1052 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1056 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1062 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1067 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1069 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1074 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1081 /* --Statusbar functions-- */
1083 build_progressbar_ascii(int percent) {
1084 int width=uzbl.gui.sbar.progress_w;
1087 GString *bar = g_string_new("");
1089 l = (double)percent*((double)width/100.);
1090 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1092 for(i=0; i<(int)l; i++)
1093 g_string_append(bar, uzbl.gui.sbar.progress_s);
1096 g_string_append(bar, uzbl.gui.sbar.progress_u);
1098 return g_string_free(bar, FALSE);
1103 const GScannerConfig scan_config = {
1106 ) /* cset_skip_characters */,
1111 ) /* cset_identifier_first */,
1118 ) /* cset_identifier_nth */,
1119 ( "" ) /* cpair_comment_single */,
1121 TRUE /* case_sensitive */,
1123 FALSE /* skip_comment_multi */,
1124 FALSE /* skip_comment_single */,
1125 FALSE /* scan_comment_multi */,
1126 TRUE /* scan_identifier */,
1127 TRUE /* scan_identifier_1char */,
1128 FALSE /* scan_identifier_NULL */,
1129 TRUE /* scan_symbols */,
1130 FALSE /* scan_binary */,
1131 FALSE /* scan_octal */,
1132 FALSE /* scan_float */,
1133 FALSE /* scan_hex */,
1134 FALSE /* scan_hex_dollar */,
1135 FALSE /* scan_string_sq */,
1136 FALSE /* scan_string_dq */,
1137 TRUE /* numbers_2_int */,
1138 FALSE /* int_2_float */,
1139 FALSE /* identifier_2_string */,
1140 FALSE /* char_2_token */,
1141 FALSE /* symbol_2_token */,
1142 TRUE /* scope_0_fallback */,
1147 uzbl.scan = g_scanner_new(&scan_config);
1148 while(symp->symbol_name) {
1149 g_scanner_scope_add_symbol(uzbl.scan, 0,
1151 GINT_TO_POINTER(symp->symbol_token));
1157 expand_template(const char *template, gboolean escape_markup) {
1158 if(!template) return NULL;
1160 GTokenType token = G_TOKEN_NONE;
1161 GString *ret = g_string_new("");
1165 g_scanner_input_text(uzbl.scan, template, strlen(template));
1166 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1167 token = g_scanner_get_next_token(uzbl.scan);
1169 if(token == G_TOKEN_SYMBOL) {
1170 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1174 buf = uzbl.state.uri?
1175 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1176 g_string_append(ret, buf);
1180 g_string_append(ret, uzbl.state.uri?
1181 uzbl.state.uri:g_strdup(""));
1184 buf = itos(uzbl.gui.sbar.load_progress);
1185 g_string_append(ret, buf);
1188 case SYM_LOADPRGSBAR:
1189 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1190 g_string_append(ret, buf);
1195 buf = uzbl.gui.main_title?
1196 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1197 g_string_append(ret, buf);
1201 g_string_append(ret, uzbl.gui.main_title?
1202 uzbl.gui.main_title:g_strdup(""));
1204 case SYM_SELECTED_URI:
1206 buf = uzbl.state.selected_url?
1207 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1208 g_string_append(ret, buf);
1212 g_string_append(ret, uzbl.state.selected_url?
1213 uzbl.state.selected_url:g_strdup(""));
1216 buf = itos(uzbl.xwin);
1217 g_string_append(ret,
1218 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1223 buf = uzbl.state.keycmd->str?
1224 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1225 g_string_append(ret, buf);
1229 g_string_append(ret, uzbl.state.keycmd->str?
1230 uzbl.state.keycmd->str:g_strdup(""));
1233 g_string_append(ret,
1234 uzbl.behave.insert_mode?
1235 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1238 g_string_append(ret,
1239 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1241 /* useragent syms */
1243 buf = itos(WEBKIT_MAJOR_VERSION);
1244 g_string_append(ret, buf);
1248 buf = itos(WEBKIT_MINOR_VERSION);
1249 g_string_append(ret, buf);
1253 buf = itos(WEBKIT_MICRO_VERSION);
1254 g_string_append(ret, buf);
1258 g_string_append(ret, uzbl.state.unameinfo.sysname);
1261 g_string_append(ret, uzbl.state.unameinfo.nodename);
1264 g_string_append(ret, uzbl.state.unameinfo.release);
1267 g_string_append(ret, uzbl.state.unameinfo.version);
1270 g_string_append(ret, uzbl.state.unameinfo.machine);
1273 g_string_append(ret, ARCH);
1276 case SYM_DOMAINNAME:
1277 g_string_append(ret, uzbl.state.unameinfo.domainname);
1281 g_string_append(ret, COMMIT);
1287 else if(token == G_TOKEN_INT) {
1288 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1289 g_string_append(ret, buf);
1292 else if(token == G_TOKEN_IDENTIFIER) {
1293 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1295 else if(token == G_TOKEN_CHAR) {
1296 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1300 return g_string_free(ret, FALSE);
1302 /* --End Statusbar functions-- */
1305 sharg_append(GArray *a, const gchar *str) {
1306 const gchar *s = (str ? str : "");
1307 g_array_append_val(a, s);
1310 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1312 run_command (const gchar *command, const guint npre, const gchar **args,
1313 const gboolean sync, char **output_stdout) {
1314 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1317 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1318 gchar *pid = itos(getpid());
1319 gchar *xwin = itos(uzbl.xwin);
1321 sharg_append(a, command);
1322 for (i = 0; i < npre; i++) /* add n args before the default vars */
1323 sharg_append(a, args[i]);
1324 sharg_append(a, uzbl.state.config_file);
1325 sharg_append(a, pid);
1326 sharg_append(a, xwin);
1327 sharg_append(a, uzbl.comm.fifo_path);
1328 sharg_append(a, uzbl.comm.socket_path);
1329 sharg_append(a, uzbl.state.uri);
1330 sharg_append(a, uzbl.gui.main_title);
1332 for (i = npre; i < g_strv_length((gchar**)args); i++)
1333 sharg_append(a, args[i]);
1337 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1339 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1340 NULL, NULL, output_stdout, NULL, NULL, &err);
1341 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1342 NULL, NULL, NULL, &err);
1344 if (uzbl.state.verbose) {
1345 GString *s = g_string_new("spawned:");
1346 for (i = 0; i < (a->len); i++) {
1347 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1348 g_string_append_printf(s, " %s", qarg);
1351 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1352 printf("%s\n", s->str);
1353 g_string_free(s, TRUE);
1355 printf("Stdout: %s\n", *output_stdout);
1359 g_printerr("error on run_command: %s\n", err->message);
1364 g_array_free (a, TRUE);
1369 split_quoted(const gchar* src, const gboolean unquote) {
1370 /* split on unquoted space, return array of strings;
1371 remove a layer of quotes and backslashes if unquote */
1372 if (!src) return NULL;
1374 gboolean dq = FALSE;
1375 gboolean sq = FALSE;
1376 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1377 GString *s = g_string_new ("");
1381 for (p = src; *p != '\0'; p++) {
1382 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1383 else if (*p == '\\') { g_string_append_c(s, *p++);
1384 g_string_append_c(s, *p); }
1385 else if ((*p == '"') && unquote && !sq) dq = !dq;
1386 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1388 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1389 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1391 else if ((*p == ' ') && !dq && !sq) {
1392 dup = g_strdup(s->str);
1393 g_array_append_val(a, dup);
1394 g_string_truncate(s, 0);
1395 } else g_string_append_c(s, *p);
1397 dup = g_strdup(s->str);
1398 g_array_append_val(a, dup);
1399 ret = (gchar**)a->data;
1400 g_array_free (a, FALSE);
1401 g_string_free (s, TRUE);
1406 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1407 (void)web_view; (void)result;
1408 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1409 if (argv_idx(argv, 0))
1410 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1414 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1415 (void)web_view; (void)result;
1417 if (argv_idx(argv, 0))
1418 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1419 TRUE, &uzbl.comm.sync_stdout);
1423 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1424 (void)web_view; (void)result;
1425 if (!uzbl.behave.shell_cmd) {
1426 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1431 gchar *spacer = g_strdup("");
1432 g_array_insert_val(argv, 1, spacer);
1433 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1435 for (i = 1; i < g_strv_length(cmd); i++)
1436 g_array_prepend_val(argv, cmd[i]);
1438 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1444 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1445 (void)web_view; (void)result;
1446 if (!uzbl.behave.shell_cmd) {
1447 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1452 gchar *spacer = g_strdup("");
1453 g_array_insert_val(argv, 1, spacer);
1454 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1456 for (i = 1; i < g_strv_length(cmd); i++)
1457 g_array_prepend_val(argv, cmd[i]);
1459 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1460 TRUE, &uzbl.comm.sync_stdout);
1466 parse_command(const char *cmd, const char *param, GString *result) {
1469 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1471 gchar **par = split_quoted(param, TRUE);
1472 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1474 if (c->no_split) { /* don't split */
1475 sharg_append(a, param);
1477 for (i = 0; i < g_strv_length(par); i++)
1478 sharg_append(a, par[i]);
1481 if (result == NULL) {
1482 GString *result_print = g_string_new("");
1484 c->function(uzbl.gui.web_view, a, result_print);
1485 if (result_print->len)
1486 printf("%*s\n", result_print->len, result_print->str);
1488 g_string_free(result_print, TRUE);
1490 c->function(uzbl.gui.web_view, a, result);
1493 g_array_free (a, TRUE);
1496 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1503 if(*uzbl.net.proxy_url == ' '
1504 || uzbl.net.proxy_url == NULL) {
1505 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1506 (GType) SOUP_SESSION_PROXY_URI);
1509 suri = soup_uri_new(uzbl.net.proxy_url);
1510 g_object_set(G_OBJECT(uzbl.net.soup_session),
1511 SOUP_SESSION_PROXY_URI,
1513 soup_uri_free(suri);
1520 if(file_exists(uzbl.gui.icon)) {
1521 if (uzbl.gui.main_window)
1522 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1524 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1526 g_free (uzbl.gui.icon);
1531 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1532 g_array_append_val (a, uzbl.state.uri);
1533 load_uri(uzbl.gui.web_view, a, NULL);
1534 g_array_free (a, TRUE);
1538 cmd_always_insert_mode() {
1539 uzbl.behave.insert_mode =
1540 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1546 g_object_set(G_OBJECT(uzbl.net.soup_session),
1547 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1551 cmd_max_conns_host() {
1552 g_object_set(G_OBJECT(uzbl.net.soup_session),
1553 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1558 soup_session_remove_feature
1559 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1560 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1561 /*g_free(uzbl.net.soup_logger);*/
1563 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1564 soup_session_add_feature(uzbl.net.soup_session,
1565 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1568 static WebKitWebSettings*
1570 return webkit_web_view_get_settings(uzbl.gui.web_view);
1575 WebKitWebSettings *ws = view_settings();
1576 if (uzbl.behave.font_size > 0) {
1577 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1580 if (uzbl.behave.monospace_size > 0) {
1581 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1582 uzbl.behave.monospace_size, NULL);
1584 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1585 uzbl.behave.font_size, NULL);
1591 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1595 cmd_disable_plugins() {
1596 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1597 !uzbl.behave.disable_plugins, NULL);
1601 cmd_disable_scripts() {
1602 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1603 !uzbl.behave.disable_scripts, NULL);
1607 cmd_minimum_font_size() {
1608 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1609 uzbl.behave.minimum_font_size, NULL);
1612 cmd_autoload_img() {
1613 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1614 uzbl.behave.autoload_img, NULL);
1619 cmd_autoshrink_img() {
1620 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1621 uzbl.behave.autoshrink_img, NULL);
1626 cmd_enable_spellcheck() {
1627 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1628 uzbl.behave.enable_spellcheck, NULL);
1632 cmd_enable_private() {
1633 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1634 uzbl.behave.enable_private, NULL);
1639 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1640 uzbl.behave.print_bg, NULL);
1645 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1646 uzbl.behave.style_uri, NULL);
1650 cmd_resizable_txt() {
1651 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1652 uzbl.behave.resizable_txt, NULL);
1656 cmd_default_encoding() {
1657 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1658 uzbl.behave.default_encoding, NULL);
1662 cmd_enforce_96dpi() {
1663 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1664 uzbl.behave.enforce_96dpi, NULL);
1668 cmd_caret_browsing() {
1669 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1670 uzbl.behave.caret_browsing, NULL);
1674 cmd_cookie_handler() {
1675 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1676 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1677 if ((g_strcmp0(split[0], "sh") == 0) ||
1678 (g_strcmp0(split[0], "spawn") == 0)) {
1679 g_free (uzbl.behave.cookie_handler);
1680 uzbl.behave.cookie_handler =
1681 g_strdup_printf("sync_%s %s", split[0], split[1]);
1688 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1693 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1698 if(uzbl.behave.inject_html) {
1699 webkit_web_view_load_html_string (uzbl.gui.web_view,
1700 uzbl.behave.inject_html, NULL);
1709 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1710 uzbl.behave.modmask = 0;
1712 if(uzbl.behave.modkey)
1713 g_free(uzbl.behave.modkey);
1714 uzbl.behave.modkey = buf;
1716 for (i = 0; modkeys[i].key != NULL; i++) {
1717 if (g_strrstr(buf, modkeys[i].key))
1718 uzbl.behave.modmask |= modkeys[i].mask;
1724 if (*uzbl.net.useragent == ' ') {
1725 g_free (uzbl.net.useragent);
1726 uzbl.net.useragent = NULL;
1728 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1730 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1731 g_free(uzbl.net.useragent);
1732 uzbl.net.useragent = ua;
1738 gtk_widget_ref(uzbl.gui.scrolled_win);
1739 gtk_widget_ref(uzbl.gui.mainbar);
1740 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1741 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1743 if(uzbl.behave.status_top) {
1744 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1745 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1748 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1749 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1751 gtk_widget_unref(uzbl.gui.scrolled_win);
1752 gtk_widget_unref(uzbl.gui.mainbar);
1753 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1758 set_var_value(gchar *name, gchar *val) {
1759 uzbl_cmdprop *c = NULL;
1763 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1764 /* check for the variable type */
1765 if (c->type == TYPE_STR) {
1766 buf = expand(val, 0);
1769 } else if(c->type == TYPE_INT) {
1770 int *ip = (int *)c->ptr;
1771 buf = expand(val, 0);
1772 *ip = (int)strtoul(buf, &endp, 10);
1774 } else if (c->type == TYPE_FLOAT) {
1775 float *fp = (float *)c->ptr;
1776 buf = expand(val, 0);
1777 *fp = strtod(buf, &endp);
1781 /* invoke a command specific function */
1782 if(c->func) c->func();
1789 Behaviour *b = &uzbl.behave;
1791 if(b->html_buffer->str) {
1792 webkit_web_view_load_html_string (uzbl.gui.web_view,
1793 b->html_buffer->str, b->base_url);
1794 g_string_free(b->html_buffer, TRUE);
1795 b->html_buffer = g_string_new("");
1799 enum {M_CMD, M_HTML};
1801 parse_cmd_line(const char *ctl_line, GString *result) {
1802 Behaviour *b = &uzbl.behave;
1805 if(b->mode == M_HTML) {
1806 len = strlen(b->html_endmarker);
1807 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1808 if(len == strlen(ctl_line)-1 &&
1809 !strncmp(b->html_endmarker, ctl_line, len)) {
1811 set_var_value("mode", "0");
1816 set_timeout(b->html_timeout);
1817 g_string_append(b->html_buffer, ctl_line);
1820 else if((ctl_line[0] == '#') /* Comments */
1821 || (ctl_line[0] == ' ')
1822 || (ctl_line[0] == '\n'))
1823 ; /* ignore these lines */
1824 else { /* parse a command */
1826 gchar **tokens = NULL;
1827 len = strlen(ctl_line);
1829 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1830 ctlstrip = g_strndup(ctl_line, len - 1);
1831 else ctlstrip = g_strdup(ctl_line);
1833 tokens = g_strsplit(ctlstrip, " ", 2);
1834 parse_command(tokens[0], tokens[1], result);
1841 build_stream_name(int type, const gchar* dir) {
1842 char *xwin_str = NULL;
1843 State *s = &uzbl.state;
1846 xwin_str = itos((int)uzbl.xwin);
1848 str = g_strdup_printf
1849 ("%s/uzbl_fifo_%s", dir,
1850 s->instance_name ? s->instance_name : xwin_str);
1851 } else if (type == SOCKET) {
1852 str = g_strdup_printf
1853 ("%s/uzbl_socket_%s", dir,
1854 s->instance_name ? s->instance_name : xwin_str );
1861 control_fifo(GIOChannel *gio, GIOCondition condition) {
1862 if (uzbl.state.verbose)
1863 printf("triggered\n");
1868 if (condition & G_IO_HUP)
1869 g_error ("Fifo: Read end of pipe died!\n");
1872 g_error ("Fifo: GIOChannel broke\n");
1874 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1875 if (ret == G_IO_STATUS_ERROR) {
1876 g_error ("Fifo: Error reading: %s\n", err->message);
1880 parse_cmd_line(ctl_line, NULL);
1887 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1888 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1889 if (unlink(uzbl.comm.fifo_path) == -1)
1890 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1891 g_free(uzbl.comm.fifo_path);
1892 uzbl.comm.fifo_path = NULL;
1895 if (*dir == ' ') { /* space unsets the variable */
1900 GIOChannel *chan = NULL;
1901 GError *error = NULL;
1902 gchar *path = build_stream_name(FIFO, dir);
1904 if (!file_exists(path)) {
1905 if (mkfifo (path, 0666) == 0) {
1906 // 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.
1907 chan = g_io_channel_new_file(path, "r+", &error);
1909 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1910 if (uzbl.state.verbose)
1911 printf ("init_fifo: created successfully as %s\n", path);
1912 uzbl.comm.fifo_path = path;
1914 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1915 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1916 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1917 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1919 /* if we got this far, there was an error; cleanup */
1920 if (error) g_error_free (error);
1927 control_stdin(GIOChannel *gio, GIOCondition condition) {
1929 gchar *ctl_line = NULL;
1932 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1933 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1936 parse_cmd_line(ctl_line, NULL);
1944 GIOChannel *chan = NULL;
1945 GError *error = NULL;
1947 chan = g_io_channel_unix_new(fileno(stdin));
1949 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1950 g_error ("Stdin: could not add watch\n");
1952 if (uzbl.state.verbose)
1953 printf ("Stdin: watch added successfully\n");
1956 g_error ("Stdin: Error while opening: %s\n", error->message);
1958 if (error) g_error_free (error);
1962 control_socket(GIOChannel *chan) {
1963 struct sockaddr_un remote;
1964 unsigned int t = sizeof(remote);
1966 GIOChannel *clientchan;
1968 clientsock = accept (g_io_channel_unix_get_fd(chan),
1969 (struct sockaddr *) &remote, &t);
1971 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1972 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1973 (GIOFunc) control_client_socket, clientchan);
1980 control_client_socket(GIOChannel *clientchan) {
1982 GString *result = g_string_new("");
1983 GError *error = NULL;
1987 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1988 if (ret == G_IO_STATUS_ERROR) {
1989 g_warning ("Error reading: %s\n", error->message);
1990 g_io_channel_shutdown(clientchan, TRUE, &error);
1992 } else if (ret == G_IO_STATUS_EOF) {
1993 /* shutdown and remove channel watch from main loop */
1994 g_io_channel_shutdown(clientchan, TRUE, &error);
1999 parse_cmd_line (ctl_line, result);
2000 g_string_append_c(result, '\n');
2001 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2003 if (ret == G_IO_STATUS_ERROR) {
2004 g_warning ("Error writing: %s", error->message);
2006 g_io_channel_flush(clientchan, &error);
2009 if (error) g_error_free (error);
2010 g_string_free(result, TRUE);
2016 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2017 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2018 if (unlink(uzbl.comm.socket_path) == -1)
2019 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2020 g_free(uzbl.comm.socket_path);
2021 uzbl.comm.socket_path = NULL;
2029 GIOChannel *chan = NULL;
2031 struct sockaddr_un local;
2032 gchar *path = build_stream_name(SOCKET, dir);
2034 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2036 local.sun_family = AF_UNIX;
2037 strcpy (local.sun_path, path);
2038 unlink (local.sun_path);
2040 len = strlen (local.sun_path) + sizeof (local.sun_family);
2041 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2042 if (uzbl.state.verbose)
2043 printf ("init_socket: opened in %s\n", path);
2046 if( (chan = g_io_channel_unix_new(sock)) ) {
2047 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2048 uzbl.comm.socket_path = path;
2051 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2053 /* if we got this far, there was an error; cleanup */
2060 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2061 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2063 // this function may be called very early when the templates are not set (yet), hence the checks
2065 update_title (void) {
2066 Behaviour *b = &uzbl.behave;
2069 if (b->show_status) {
2070 if (b->title_format_short) {
2071 parsed = expand_template(b->title_format_short, FALSE);
2072 if (uzbl.gui.main_window)
2073 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2076 if (b->status_format) {
2077 parsed = expand_template(b->status_format, TRUE);
2078 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2081 if (b->status_background) {
2083 gdk_color_parse (b->status_background, &color);
2084 //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)
2085 if (uzbl.gui.main_window)
2086 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2089 if (b->title_format_long) {
2090 parsed = expand_template(b->title_format_long, FALSE);
2091 if (uzbl.gui.main_window)
2092 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2099 key_press_cb (GtkWidget* window, GdkEventKey* event)
2101 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2105 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2106 || 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)
2109 /* turn off insert mode (if always_insert_mode is not used) */
2110 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2111 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2116 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2119 if (event->keyval == GDK_Escape) {
2120 g_string_truncate(uzbl.state.keycmd, 0);
2122 dehilight(uzbl.gui.web_view, NULL, NULL);
2126 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2127 if (event->keyval == GDK_Insert) {
2129 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2130 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2132 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2135 g_string_append (uzbl.state.keycmd, str);
2142 if (event->keyval == GDK_BackSpace)
2143 keycmd_bs(NULL, NULL, NULL);
2145 gboolean key_ret = FALSE;
2146 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2148 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2150 run_keycmd(key_ret);
2152 if (key_ret) return (!uzbl.behave.insert_mode);
2157 run_keycmd(const gboolean key_ret) {
2158 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2160 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2161 g_string_truncate(uzbl.state.keycmd, 0);
2162 parse_command(act->name, act->param, NULL);
2166 /* try if it's an incremental keycmd or one that takes args, and run it */
2167 GString* short_keys = g_string_new ("");
2168 GString* short_keys_inc = g_string_new ("");
2170 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2171 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2172 g_string_assign(short_keys_inc, short_keys->str);
2173 g_string_append_c(short_keys, '_');
2174 g_string_append_c(short_keys_inc, '*');
2176 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2177 /* run normal cmds only if return was pressed */
2178 exec_paramcmd(act, i);
2179 g_string_truncate(uzbl.state.keycmd, 0);
2181 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2182 if (key_ret) /* just quit the incremental command on return */
2183 g_string_truncate(uzbl.state.keycmd, 0);
2184 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2188 g_string_truncate(short_keys, short_keys->len - 1);
2190 g_string_free (short_keys, TRUE);
2191 g_string_free (short_keys_inc, TRUE);
2195 exec_paramcmd(const Action *act, const guint i) {
2196 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2197 GString *actionname = g_string_new ("");
2198 GString *actionparam = g_string_new ("");
2199 g_string_erase (parampart, 0, i+1);
2201 g_string_printf (actionname, act->name, parampart->str);
2203 g_string_printf (actionparam, act->param, parampart->str);
2204 parse_command(actionname->str, actionparam->str, NULL);
2205 g_string_free(actionname, TRUE);
2206 g_string_free(actionparam, TRUE);
2207 g_string_free(parampart, TRUE);
2215 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2216 //main_window_ref = g_object_ref(scrolled_window);
2217 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
2219 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2220 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2222 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2223 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2224 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2225 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2226 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2227 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2228 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2229 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2230 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2231 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2232 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2234 return scrolled_window;
2241 g->mainbar = gtk_hbox_new (FALSE, 0);
2243 /* keep a reference to the bar so we can re-pack it at runtime*/
2244 //sbar_ref = g_object_ref(g->mainbar);
2246 g->mainbar_label = gtk_label_new ("");
2247 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2248 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2249 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2250 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2251 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2252 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2257 GtkWidget* create_window () {
2258 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2259 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2260 gtk_widget_set_name (window, "Uzbl browser");
2261 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2262 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2268 GtkPlug* create_plug () {
2269 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2270 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2271 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2278 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2280 If actname is one that calls an external command, this function will inject
2281 newargs in front of the user-provided args in that command line. They will
2282 come become after the body of the script (in sh) or after the name of
2283 the command to execute (in spawn).
2284 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2285 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2287 The return value consist of two strings: the action (sh, ...) and its args.
2289 If act is not one that calls an external command, then the given action merely
2292 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2293 gchar *actdup = g_strdup(actname);
2294 g_array_append_val(rets, actdup);
2296 if ((g_strcmp0(actname, "spawn") == 0) ||
2297 (g_strcmp0(actname, "sh") == 0) ||
2298 (g_strcmp0(actname, "sync_spawn") == 0) ||
2299 (g_strcmp0(actname, "sync_sh") == 0)) {
2301 GString *a = g_string_new("");
2302 gchar **spawnparts = split_quoted(origargs, FALSE);
2303 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2304 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2306 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2307 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2309 g_array_append_val(rets, a->str);
2310 g_string_free(a, FALSE);
2311 g_strfreev(spawnparts);
2313 gchar *origdup = g_strdup(origargs);
2314 g_array_append_val(rets, origdup);
2316 return (gchar**)g_array_free(rets, FALSE);
2320 run_handler (const gchar *act, const gchar *args) {
2321 /* Consider this code a temporary hack to make the handlers usable.
2322 In practice, all this splicing, injection, and reconstruction is
2323 inefficient, annoying and hard to manage. Potential pitfalls arise
2324 when the handler specific args 1) are not quoted (the handler
2325 callbacks should take care of this) 2) are quoted but interfere
2326 with the users' own quotation. A more ideal solution is
2327 to refactor parse_command so that it doesn't just take a string
2328 and execute it; rather than that, we should have a function which
2329 returns the argument vector parsed from the string. This vector
2330 could be modified (e.g. insert additional args into it) before
2331 passing it to the next function that actually executes it. Though
2332 it still isn't perfect for chain actions.. will reconsider & re-
2333 factor when I have the time. -duc */
2335 char **parts = g_strsplit(act, " ", 2);
2337 if (g_strcmp0(parts[0], "chain") == 0) {
2338 GString *newargs = g_string_new("");
2339 gchar **chainparts = split_quoted(parts[1], FALSE);
2341 /* for every argument in the chain, inject the handler args
2342 and make sure the new parts are wrapped in quotes */
2343 gchar **cp = chainparts;
2345 gchar *quotless = NULL;
2346 gchar **spliced_quotless = NULL; // sigh -_-;
2347 gchar **inpart = NULL;
2350 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2352 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2353 } else quotless = g_strdup(*cp);
2355 spliced_quotless = g_strsplit(quotless, " ", 2);
2356 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2357 g_strfreev(spliced_quotless);
2359 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2365 parse_command(parts[0], &(newargs->str[1]), NULL);
2366 g_string_free(newargs, TRUE);
2367 g_strfreev(chainparts);
2370 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2371 parse_command(inparts[0], inparts[1], NULL);
2379 add_binding (const gchar *key, const gchar *act) {
2380 char **parts = g_strsplit(act, " ", 2);
2387 if (uzbl.state.verbose)
2388 printf ("Binding %-10s : %s\n", key, act);
2389 action = new_action(parts[0], parts[1]);
2391 if (g_hash_table_remove (uzbl.bindings, key))
2392 g_warning ("Overwriting existing binding for \"%s\"", key);
2393 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2398 get_xdg_var (XDG_Var xdg) {
2399 const gchar* actual_value = getenv (xdg.environmental);
2400 const gchar* home = getenv ("HOME");
2401 gchar* return_value;
2403 if (! actual_value || strcmp (actual_value, "") == 0) {
2404 if (xdg.default_value) {
2405 return_value = str_replace ("~", home, xdg.default_value);
2407 return_value = NULL;
2410 return_value = str_replace("~", home, actual_value);
2413 return return_value;
2417 find_xdg_file (int xdg_type, char* filename) {
2418 /* xdg_type = 0 => config
2419 xdg_type = 1 => data
2420 xdg_type = 2 => cache*/
2422 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2423 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2426 gchar* temporary_string;
2430 if (! file_exists (temporary_file) && xdg_type != 2) {
2431 buf = get_xdg_var (XDG[3 + xdg_type]);
2432 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2435 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2436 g_free (temporary_file);
2437 temporary_file = g_strconcat (temporary_string, filename, NULL);
2441 //g_free (temporary_string); - segfaults.
2443 if (file_exists (temporary_file)) {
2444 return temporary_file;
2451 State *s = &uzbl.state;
2452 Network *n = &uzbl.net;
2454 for (i = 0; default_config[i].command != NULL; i++) {
2455 parse_cmd_line(default_config[i].command, NULL);
2458 if (g_strcmp0(s->config_file, "-") == 0) {
2459 s->config_file = NULL;
2463 if (!s->config_file) {
2464 s->config_file = find_xdg_file (0, "/uzbl/config");
2467 if (s->config_file) {
2468 GArray* lines = read_file_by_line (s->config_file);
2472 while ((line = g_array_index(lines, gchar*, i))) {
2473 parse_cmd_line (line, NULL);
2477 g_array_free (lines, TRUE);
2479 if (uzbl.state.verbose)
2480 printf ("No configuration file loaded.\n");
2483 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2486 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2489 if (!uzbl.behave.cookie_handler)
2492 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2493 GString *s = g_string_new ("");
2494 SoupURI * soup_uri = soup_message_get_uri(msg);
2495 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2496 run_handler(uzbl.behave.cookie_handler, s->str);
2498 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2499 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2500 if ( p != NULL ) *p = '\0';
2501 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2503 if (uzbl.comm.sync_stdout)
2504 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2506 g_string_free(s, TRUE);
2510 save_cookies (SoupMessage *msg, gpointer user_data){
2514 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2515 cookie = soup_cookie_to_set_cookie_header(ck->data);
2516 SoupURI * soup_uri = soup_message_get_uri(msg);
2517 GString *s = g_string_new ("");
2518 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2519 run_handler(uzbl.behave.cookie_handler, s->str);
2521 g_string_free(s, TRUE);
2526 /* --- WEBINSPECTOR --- */
2528 hide_window_cb(GtkWidget *widget, gpointer data) {
2531 gtk_widget_hide(widget);
2534 static WebKitWebView*
2535 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2538 (void) web_inspector;
2539 GtkWidget* scrolled_window;
2540 GtkWidget* new_web_view;
2543 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2544 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2545 G_CALLBACK(hide_window_cb), NULL);
2547 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2548 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2549 gtk_widget_show(g->inspector_window);
2551 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2552 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2553 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2554 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2555 gtk_widget_show(scrolled_window);
2557 new_web_view = webkit_web_view_new();
2558 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2560 return WEBKIT_WEB_VIEW(new_web_view);
2564 inspector_show_window_cb (WebKitWebInspector* inspector){
2566 gtk_widget_show(uzbl.gui.inspector_window);
2570 /* TODO: Add variables and code to make use of these functions */
2572 inspector_close_window_cb (WebKitWebInspector* inspector){
2578 inspector_attach_window_cb (WebKitWebInspector* inspector){
2584 inspector_detach_window_cb (WebKitWebInspector* inspector){
2590 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2596 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2602 set_up_inspector() {
2604 WebKitWebSettings *settings = view_settings();
2605 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2607 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2608 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2609 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2610 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2611 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2612 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2613 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2615 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2619 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2621 uzbl_cmdprop *c = v;
2626 if(c->type == TYPE_STR)
2627 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2628 else if(c->type == TYPE_INT)
2629 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2633 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2637 printf("bind %s = %s %s\n", (char *)k ,
2638 (char *)a->name, a->param?(char *)a->param:"");
2643 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2644 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2649 main (int argc, char* argv[]) {
2650 gtk_init (&argc, &argv);
2651 if (!g_thread_supported ())
2652 g_thread_init (NULL);
2653 uzbl.state.executable_path = g_strdup(argv[0]);
2654 uzbl.state.selected_url = NULL;
2655 uzbl.state.searchtx = NULL;
2657 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2658 g_option_context_add_main_entries (context, entries, NULL);
2659 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2660 g_option_context_parse (context, &argc, &argv, NULL);
2661 g_option_context_free(context);
2663 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2664 gboolean verbose_override = uzbl.state.verbose;
2666 /* initialize hash table */
2667 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2669 uzbl.net.soup_session = webkit_get_default_session();
2670 uzbl.state.keycmd = g_string_new("");
2672 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2673 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2674 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2675 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2676 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2677 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2680 if(uname(&uzbl.state.unameinfo) == -1)
2681 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2683 uzbl.gui.sbar.progress_s = g_strdup("=");
2684 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2685 uzbl.gui.sbar.progress_w = 10;
2687 /* HTML mode defaults*/
2688 uzbl.behave.html_buffer = g_string_new("");
2689 uzbl.behave.html_endmarker = g_strdup(".");
2690 uzbl.behave.html_timeout = 60;
2691 uzbl.behave.base_url = g_strdup("http://invalid");
2693 /* default mode indicators */
2694 uzbl.behave.insert_indicator = g_strdup("I");
2695 uzbl.behave.cmd_indicator = g_strdup("C");
2699 make_var_to_name_hash();
2701 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2703 uzbl.gui.scrolled_win = create_browser();
2706 /* initial packing */
2707 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2708 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2710 if (uzbl.state.socket_id) {
2711 uzbl.gui.plug = create_plug ();
2712 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2713 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2715 uzbl.gui.main_window = create_window ();
2716 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2717 gtk_widget_show_all (uzbl.gui.main_window);
2718 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2721 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2723 if (uzbl.state.verbose) {
2724 printf("Uzbl start location: %s\n", argv[0]);
2725 if (uzbl.state.socket_id)
2726 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2728 printf("window_id %i\n",(int) uzbl.xwin);
2729 printf("pid %i\n", getpid ());
2730 printf("name: %s\n", uzbl.state.instance_name);
2733 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2734 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2735 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2736 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2737 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2741 if (!uzbl.behave.show_status)
2742 gtk_widget_hide(uzbl.gui.mainbar);
2749 if (verbose_override > uzbl.state.verbose)
2750 uzbl.state.verbose = verbose_override;
2753 set_var_value("uri", uri_override);
2754 g_free(uri_override);
2755 } else if (uzbl.state.uri)
2756 cmd_load_uri(uzbl.gui.web_view, NULL);
2761 return EXIT_SUCCESS;
2764 /* vi: set et ts=4: */