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};
197 get_exp_type(gchar *s) {
201 else if(*(s+1) == '{')
202 return EXP_BRACED_VAR;
204 return EXP_SIMPLE_VAR;
209 /* setting 'recurse = 1' will prevent expand() from
210 * expanding '@(command)'
213 expand(char *s, gboolean recurse) {
220 gchar *cmd_stdout = NULL;
222 GString *buf = g_string_new("");
227 g_string_append_c(buf, *++s);
232 etype = get_exp_type(s);
247 if( (vend = strchr(s, upto)) ||
248 (vend = strchr(s, '\0')) ) {
249 strncpy(ret, s, vend-s);
253 if(etype == EXP_SIMPLE_VAR ||
254 etype == EXP_BRACED_VAR) {
255 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
256 if(c->type == TYPE_STR)
257 g_string_append(buf, (gchar *)*c->ptr);
258 else if(c->type == TYPE_INT) {
259 char *b = itos((int)*c->ptr);
260 g_string_append(buf, b);
264 if(upto == ' ') s = vend;
269 mycmd = expand(ret, 1);
270 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
274 g_printerr("error on running command: %s\n", err->message);
277 else if (*cmd_stdout) {
278 g_string_append(buf, cmd_stdout);
286 g_string_append_c(buf, *s);
291 return g_string_free(buf, FALSE);
298 snprintf(tmp, sizeof(tmp), "%i", val);
299 return g_strdup(tmp);
303 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
306 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
309 str_replace (const char* search, const char* replace, const char* string) {
313 buf = g_strsplit (string, search, -1);
314 ret = g_strjoinv (replace, buf);
315 g_strfreev(buf); // somebody said this segfaults
321 read_file_by_line (gchar *path) {
322 GIOChannel *chan = NULL;
323 gchar *readbuf = NULL;
325 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
328 chan = g_io_channel_new_file(path, "r", NULL);
331 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
332 const gchar* val = g_strdup (readbuf);
333 g_array_append_val (lines, val);
338 g_io_channel_unref (chan);
340 fprintf(stderr, "File '%s' not be read.\n", path);
347 gchar* parseenv (char* string) {
348 extern char** environ;
349 gchar* tmpstr = NULL;
353 while (environ[i] != NULL) {
354 gchar** env = g_strsplit (environ[i], "=", 2);
355 gchar* envname = g_strconcat ("$", env[0], NULL);
357 if (g_strrstr (string, envname) != NULL) {
358 tmpstr = g_strdup(string);
360 string = str_replace(envname, env[1], tmpstr);
365 g_strfreev (env); // somebody said this breaks uzbl
373 setup_signal(int signr, sigfunc *shandler) {
374 struct sigaction nh, oh;
376 nh.sa_handler = shandler;
377 sigemptyset(&nh.sa_mask);
380 if(sigaction(signr, &nh, &oh) < 0)
388 if (uzbl.behave.fifo_dir)
389 unlink (uzbl.comm.fifo_path);
390 if (uzbl.behave.socket_dir)
391 unlink (uzbl.comm.socket_path);
393 g_free(uzbl.state.executable_path);
394 g_string_free(uzbl.state.keycmd, TRUE);
395 g_hash_table_destroy(uzbl.bindings);
396 g_hash_table_destroy(uzbl.behave.commands);
399 /* used for html_mode_timeout
400 * be sure to extend this function to use
401 * more timers if needed in other places
404 set_timeout(int seconds) {
406 memset(&t, 0, sizeof t);
408 t.it_value.tv_sec = seconds;
409 t.it_value.tv_usec = 0;
410 setitimer(ITIMER_REAL, &t, NULL);
413 /* --- SIGNAL HANDLER --- */
416 catch_sigterm(int s) {
422 catch_sigint(int s) {
432 set_var_value("mode", "0");
437 /* --- CALLBACKS --- */
440 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
443 (void) navigation_action;
444 (void) policy_decision;
446 const gchar* uri = webkit_network_request_get_uri (request);
447 if (uzbl.state.verbose)
448 printf("New window requested -> %s \n", uri);
449 new_window_load_uri(uri);
454 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
459 /* If we can display it, let's display it... */
460 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
461 webkit_web_policy_decision_use (policy_decision);
465 /* ...everything we can't displayed is downloaded */
466 webkit_web_policy_decision_download (policy_decision);
471 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
475 if (uzbl.state.selected_url != NULL) {
476 if (uzbl.state.verbose)
477 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
478 new_window_load_uri(uzbl.state.selected_url);
480 if (uzbl.state.verbose)
481 printf("New web view -> %s\n","Nothing to open, exiting");
487 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
490 if (uzbl.behave.download_handler) {
491 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
492 if (uzbl.state.verbose)
493 printf("Download -> %s\n",uri);
494 /* if urls not escaped, we may have to escape and quote uri before this call */
495 run_handler(uzbl.behave.download_handler, uri);
500 /* scroll a bar in a given direction */
502 scroll (GtkAdjustment* bar, GArray *argv) {
506 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
507 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
508 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
512 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
513 (void) page; (void) argv; (void) result;
514 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
518 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
519 (void) page; (void) argv; (void) result;
520 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
521 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
525 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
526 (void) page; (void) result;
527 scroll(uzbl.gui.bar_v, argv);
531 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
532 (void) page; (void) result;
533 scroll(uzbl.gui.bar_h, argv);
538 if (!uzbl.behave.show_status) {
539 gtk_widget_hide(uzbl.gui.mainbar);
541 gtk_widget_show(uzbl.gui.mainbar);
547 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
552 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
556 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
561 if (uzbl.behave.show_status) {
562 gtk_widget_hide(uzbl.gui.mainbar);
564 gtk_widget_show(uzbl.gui.mainbar);
566 uzbl.behave.show_status = !uzbl.behave.show_status;
571 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
575 //Set selected_url state variable
576 g_free(uzbl.state.selected_url);
577 uzbl.state.selected_url = NULL;
579 uzbl.state.selected_url = g_strdup(link);
585 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
589 if (uzbl.gui.main_title)
590 g_free (uzbl.gui.main_title);
591 uzbl.gui.main_title = g_strdup (title);
596 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
599 uzbl.gui.sbar.load_progress = progress;
604 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
608 if (uzbl.behave.load_finish_handler)
609 run_handler(uzbl.behave.load_finish_handler, "");
613 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
617 uzbl.gui.sbar.load_progress = 0;
618 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
619 if (uzbl.behave.load_start_handler)
620 run_handler(uzbl.behave.load_start_handler, "");
624 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
627 g_free (uzbl.state.uri);
628 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
629 uzbl.state.uri = g_string_free (newuri, FALSE);
630 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
631 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
634 if (uzbl.behave.load_commit_handler)
635 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
639 destroy_cb (GtkWidget* widget, gpointer data) {
647 if (uzbl.behave.history_handler) {
649 struct tm * timeinfo;
652 timeinfo = localtime ( &rawtime );
653 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
654 run_handler(uzbl.behave.history_handler, date);
659 /* VIEW funcs (little webkit wrappers) */
660 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
662 VIEWFUNC(reload_bypass_cache)
663 VIEWFUNC(stop_loading)
670 /* -- command to callback/function map for things we cannot attach to any signals */
671 static struct {char *key; CommandInfo value;} cmdlist[] =
672 { /* key function no_split */
673 { "back", {view_go_back, 0} },
674 { "forward", {view_go_forward, 0} },
675 { "scroll_vert", {scroll_vert, 0} },
676 { "scroll_horz", {scroll_horz, 0} },
677 { "scroll_begin", {scroll_begin, 0} },
678 { "scroll_end", {scroll_end, 0} },
679 { "reload", {view_reload, 0}, },
680 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
681 { "stop", {view_stop_loading, 0}, },
682 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
683 { "zoom_out", {view_zoom_out, 0}, },
684 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
685 { "uri", {load_uri, TRUE} },
686 { "js", {run_js, TRUE} },
687 { "script", {run_external_js, 0} },
688 { "toggle_status", {toggle_status_cb, 0} },
689 { "spawn", {spawn, 0} },
690 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
691 { "sh", {spawn_sh, 0} },
692 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
693 { "exit", {close_uzbl, 0} },
694 { "search", {search_forward_text, TRUE} },
695 { "search_reverse", {search_reverse_text, TRUE} },
696 { "dehilight", {dehilight, 0} },
697 { "toggle_insert_mode", {toggle_insert_mode, 0} },
698 { "set", {set_var, TRUE} },
699 //{ "get", {get_var, TRUE} },
700 { "bind", {act_bind, TRUE} },
701 { "dump_config", {act_dump_config, 0} },
702 { "keycmd", {keycmd, TRUE} },
703 { "keycmd_nl", {keycmd_nl, TRUE} },
704 { "keycmd_bs", {keycmd_bs, 0} },
705 { "chain", {chain, 0} },
706 { "print", {print, TRUE} }
713 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
715 for (i = 0; i < LENGTH(cmdlist); i++)
716 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
719 /* -- CORE FUNCTIONS -- */
722 free_action(gpointer act) {
723 Action *action = (Action*)act;
724 g_free(action->name);
726 g_free(action->param);
731 new_action(const gchar *name, const gchar *param) {
732 Action *action = g_new(Action, 1);
734 action->name = g_strdup(name);
736 action->param = g_strdup(param);
738 action->param = NULL;
744 file_exists (const char * filename) {
745 return (access(filename, F_OK) == 0);
749 set_var(WebKitWebView *page, GArray *argv, GString *result) {
750 (void) page; (void) result;
751 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
752 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
753 set_var_value(g_strstrip(split[0]), value);
759 print(WebKitWebView *page, GArray *argv, GString *result) {
760 (void) page; (void) result;
763 buf = expand(argv_idx(argv, 0), 0);
764 g_string_assign(result, buf);
769 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
770 (void) page; (void) result;
771 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
772 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
773 add_binding(g_strstrip(split[0]), value);
785 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
786 (void) page; (void) result;
788 if (argv_idx(argv, 0)) {
789 if (strcmp (argv_idx(argv, 0), "0") == 0) {
790 uzbl.behave.insert_mode = FALSE;
792 uzbl.behave.insert_mode = TRUE;
795 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
802 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
805 if (argv_idx(argv, 0)) {
806 GString* newuri = g_string_new (argv_idx(argv, 0));
807 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
808 run_js(web_view, argv, NULL);
811 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
812 g_string_prepend (newuri, "http://");
813 /* if we do handle cookies, ask our handler for them */
814 webkit_web_view_load_uri (web_view, newuri->str);
815 g_string_free (newuri, TRUE);
823 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
824 size_t argumentCount, const JSValueRef arguments[],
825 JSValueRef* exception) {
830 JSStringRef js_result_string;
831 GString *result = g_string_new("");
833 if (argumentCount >= 1) {
834 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
835 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
836 char ctl_line[arg_size];
837 JSStringGetUTF8CString(arg, ctl_line, arg_size);
839 parse_cmd_line(ctl_line, result);
841 JSStringRelease(arg);
843 js_result_string = JSStringCreateWithUTF8CString(result->str);
845 g_string_free(result, TRUE);
847 return JSValueMakeString(ctx, js_result_string);
850 static JSStaticFunction js_static_functions[] = {
851 {"run", js_run_command, kJSPropertyAttributeNone},
856 /* This function creates the class and its definition, only once */
857 if (!uzbl.js.initialized) {
858 /* it would be pretty cool to make this dynamic */
859 uzbl.js.classdef = kJSClassDefinitionEmpty;
860 uzbl.js.classdef.staticFunctions = js_static_functions;
862 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
868 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
869 WebKitWebFrame *frame;
870 JSGlobalContextRef context;
871 JSObjectRef globalobject;
872 JSStringRef var_name;
874 JSStringRef js_script;
875 JSValueRef js_result;
876 JSStringRef js_result_string;
877 size_t js_result_size;
881 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
882 context = webkit_web_frame_get_global_context(frame);
883 globalobject = JSContextGetGlobalObject(context);
885 /* uzbl javascript namespace */
886 var_name = JSStringCreateWithUTF8CString("Uzbl");
887 JSObjectSetProperty(context, globalobject, var_name,
888 JSObjectMake(context, uzbl.js.classref, NULL),
889 kJSClassAttributeNone, NULL);
891 /* evaluate the script and get return value*/
892 js_script = JSStringCreateWithUTF8CString(script);
893 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
894 if (js_result && !JSValueIsUndefined(context, js_result)) {
895 js_result_string = JSValueToStringCopy(context, js_result, NULL);
896 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
898 if (js_result_size) {
899 char js_result_utf8[js_result_size];
900 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
901 g_string_assign(result, js_result_utf8);
904 JSStringRelease(js_result_string);
908 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
910 JSStringRelease(var_name);
911 JSStringRelease(js_script);
915 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
917 if (argv_idx(argv, 0))
918 eval_js(web_view, argv_idx(argv, 0), result);
922 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
924 if (argv_idx(argv, 0)) {
925 GArray* lines = read_file_by_line (argv_idx (argv, 0));
930 while ((line = g_array_index(lines, gchar*, i))) {
932 js = g_strdup (line);
934 gchar* newjs = g_strconcat (js, line, NULL);
941 if (uzbl.state.verbose)
942 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
944 if (argv_idx (argv, 1)) {
945 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
949 eval_js (web_view, js, result);
951 g_array_free (lines, TRUE);
956 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
957 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
958 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
959 webkit_web_view_unmark_text_matches (page);
960 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
961 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
965 if (uzbl.state.searchtx) {
966 if (uzbl.state.verbose)
967 printf ("Searching: %s\n", uzbl.state.searchtx);
968 webkit_web_view_set_highlight_text_matches (page, TRUE);
969 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
974 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
976 search_text(page, argv, TRUE);
980 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
982 search_text(page, argv, FALSE);
986 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
987 (void) argv; (void) result;
988 webkit_web_view_set_highlight_text_matches (page, FALSE);
993 new_window_load_uri (const gchar * uri) {
994 GString* to_execute = g_string_new ("");
995 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
997 for (i = 0; entries[i].long_name != NULL; i++) {
998 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
999 gchar** str = (gchar**)entries[i].arg_data;
1001 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1005 if (uzbl.state.verbose)
1006 printf("\n%s\n", to_execute->str);
1007 g_spawn_command_line_async (to_execute->str, NULL);
1008 g_string_free (to_execute, TRUE);
1012 chain (WebKitWebView *page, GArray *argv, GString *result) {
1013 (void) page; (void) result;
1015 gchar **parts = NULL;
1017 while ((a = argv_idx(argv, i++))) {
1018 parts = g_strsplit (a, " ", 2);
1019 parse_command(parts[0], parts[1], result);
1025 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1029 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1035 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1039 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1045 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1050 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1052 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1057 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1064 /* --Statusbar functions-- */
1066 build_progressbar_ascii(int percent) {
1067 int width=uzbl.gui.sbar.progress_w;
1070 GString *bar = g_string_new("");
1072 l = (double)percent*((double)width/100.);
1073 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1075 for(i=0; i<(int)l; i++)
1076 g_string_append(bar, uzbl.gui.sbar.progress_s);
1079 g_string_append(bar, uzbl.gui.sbar.progress_u);
1081 return g_string_free(bar, FALSE);
1086 const GScannerConfig scan_config = {
1089 ) /* cset_skip_characters */,
1094 ) /* cset_identifier_first */,
1101 ) /* cset_identifier_nth */,
1102 ( "" ) /* cpair_comment_single */,
1104 TRUE /* case_sensitive */,
1106 FALSE /* skip_comment_multi */,
1107 FALSE /* skip_comment_single */,
1108 FALSE /* scan_comment_multi */,
1109 TRUE /* scan_identifier */,
1110 TRUE /* scan_identifier_1char */,
1111 FALSE /* scan_identifier_NULL */,
1112 TRUE /* scan_symbols */,
1113 FALSE /* scan_binary */,
1114 FALSE /* scan_octal */,
1115 FALSE /* scan_float */,
1116 FALSE /* scan_hex */,
1117 FALSE /* scan_hex_dollar */,
1118 FALSE /* scan_string_sq */,
1119 FALSE /* scan_string_dq */,
1120 TRUE /* numbers_2_int */,
1121 FALSE /* int_2_float */,
1122 FALSE /* identifier_2_string */,
1123 FALSE /* char_2_token */,
1124 FALSE /* symbol_2_token */,
1125 TRUE /* scope_0_fallback */,
1130 uzbl.scan = g_scanner_new(&scan_config);
1131 while(symp->symbol_name) {
1132 g_scanner_scope_add_symbol(uzbl.scan, 0,
1134 GINT_TO_POINTER(symp->symbol_token));
1140 expand_template(const char *template, gboolean escape_markup) {
1141 if(!template) return NULL;
1143 GTokenType token = G_TOKEN_NONE;
1144 GString *ret = g_string_new("");
1148 g_scanner_input_text(uzbl.scan, template, strlen(template));
1149 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1150 token = g_scanner_get_next_token(uzbl.scan);
1152 if(token == G_TOKEN_SYMBOL) {
1153 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1157 buf = uzbl.state.uri?
1158 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1159 g_string_append(ret, buf);
1163 g_string_append(ret, uzbl.state.uri?
1164 uzbl.state.uri:g_strdup(""));
1167 buf = itos(uzbl.gui.sbar.load_progress);
1168 g_string_append(ret, buf);
1171 case SYM_LOADPRGSBAR:
1172 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1173 g_string_append(ret, buf);
1178 buf = uzbl.gui.main_title?
1179 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1180 g_string_append(ret, buf);
1184 g_string_append(ret, uzbl.gui.main_title?
1185 uzbl.gui.main_title:g_strdup(""));
1187 case SYM_SELECTED_URI:
1189 buf = uzbl.state.selected_url?
1190 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1191 g_string_append(ret, buf);
1195 g_string_append(ret, uzbl.state.selected_url?
1196 uzbl.state.selected_url:g_strdup(""));
1199 buf = itos(uzbl.xwin);
1200 g_string_append(ret,
1201 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1206 buf = uzbl.state.keycmd->str?
1207 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1208 g_string_append(ret, buf);
1212 g_string_append(ret, uzbl.state.keycmd->str?
1213 uzbl.state.keycmd->str:g_strdup(""));
1216 g_string_append(ret,
1217 uzbl.behave.insert_mode?
1218 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1221 g_string_append(ret,
1222 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1224 /* useragent syms */
1226 buf = itos(WEBKIT_MAJOR_VERSION);
1227 g_string_append(ret, buf);
1231 buf = itos(WEBKIT_MINOR_VERSION);
1232 g_string_append(ret, buf);
1236 buf = itos(WEBKIT_MICRO_VERSION);
1237 g_string_append(ret, buf);
1241 g_string_append(ret, uzbl.state.unameinfo.sysname);
1244 g_string_append(ret, uzbl.state.unameinfo.nodename);
1247 g_string_append(ret, uzbl.state.unameinfo.release);
1250 g_string_append(ret, uzbl.state.unameinfo.version);
1253 g_string_append(ret, uzbl.state.unameinfo.machine);
1256 g_string_append(ret, ARCH);
1259 case SYM_DOMAINNAME:
1260 g_string_append(ret, uzbl.state.unameinfo.domainname);
1264 g_string_append(ret, COMMIT);
1270 else if(token == G_TOKEN_INT) {
1271 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1272 g_string_append(ret, buf);
1275 else if(token == G_TOKEN_IDENTIFIER) {
1276 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1278 else if(token == G_TOKEN_CHAR) {
1279 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1283 return g_string_free(ret, FALSE);
1285 /* --End Statusbar functions-- */
1288 sharg_append(GArray *a, const gchar *str) {
1289 const gchar *s = (str ? str : "");
1290 g_array_append_val(a, s);
1293 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1295 run_command (const gchar *command, const guint npre, const gchar **args,
1296 const gboolean sync, char **output_stdout) {
1297 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1300 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1301 gchar *pid = itos(getpid());
1302 gchar *xwin = itos(uzbl.xwin);
1304 sharg_append(a, command);
1305 for (i = 0; i < npre; i++) /* add n args before the default vars */
1306 sharg_append(a, args[i]);
1307 sharg_append(a, uzbl.state.config_file);
1308 sharg_append(a, pid);
1309 sharg_append(a, xwin);
1310 sharg_append(a, uzbl.comm.fifo_path);
1311 sharg_append(a, uzbl.comm.socket_path);
1312 sharg_append(a, uzbl.state.uri);
1313 sharg_append(a, uzbl.gui.main_title);
1315 for (i = npre; i < g_strv_length((gchar**)args); i++)
1316 sharg_append(a, args[i]);
1320 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1322 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1323 NULL, NULL, output_stdout, NULL, NULL, &err);
1324 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1325 NULL, NULL, NULL, &err);
1327 if (uzbl.state.verbose) {
1328 GString *s = g_string_new("spawned:");
1329 for (i = 0; i < (a->len); i++) {
1330 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1331 g_string_append_printf(s, " %s", qarg);
1334 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1335 printf("%s\n", s->str);
1336 g_string_free(s, TRUE);
1338 printf("Stdout: %s\n", *output_stdout);
1342 g_printerr("error on run_command: %s\n", err->message);
1347 g_array_free (a, TRUE);
1352 split_quoted(const gchar* src, const gboolean unquote) {
1353 /* split on unquoted space, return array of strings;
1354 remove a layer of quotes and backslashes if unquote */
1355 if (!src) return NULL;
1357 gboolean dq = FALSE;
1358 gboolean sq = FALSE;
1359 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1360 GString *s = g_string_new ("");
1364 for (p = src; *p != '\0'; p++) {
1365 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1366 else if (*p == '\\') { g_string_append_c(s, *p++);
1367 g_string_append_c(s, *p); }
1368 else if ((*p == '"') && unquote && !sq) dq = !dq;
1369 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1371 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1372 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1374 else if ((*p == ' ') && !dq && !sq) {
1375 dup = g_strdup(s->str);
1376 g_array_append_val(a, dup);
1377 g_string_truncate(s, 0);
1378 } else g_string_append_c(s, *p);
1380 dup = g_strdup(s->str);
1381 g_array_append_val(a, dup);
1382 ret = (gchar**)a->data;
1383 g_array_free (a, FALSE);
1384 g_string_free (s, TRUE);
1389 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1390 (void)web_view; (void)result;
1391 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1392 if (argv_idx(argv, 0))
1393 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1397 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1398 (void)web_view; (void)result;
1400 if (argv_idx(argv, 0))
1401 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1402 TRUE, &uzbl.comm.sync_stdout);
1406 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1407 (void)web_view; (void)result;
1408 if (!uzbl.behave.shell_cmd) {
1409 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1414 gchar *spacer = g_strdup("");
1415 g_array_insert_val(argv, 1, spacer);
1416 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1418 for (i = 1; i < g_strv_length(cmd); i++)
1419 g_array_prepend_val(argv, cmd[i]);
1421 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1427 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1428 (void)web_view; (void)result;
1429 if (!uzbl.behave.shell_cmd) {
1430 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1435 gchar *spacer = g_strdup("");
1436 g_array_insert_val(argv, 1, spacer);
1437 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1439 for (i = 1; i < g_strv_length(cmd); i++)
1440 g_array_prepend_val(argv, cmd[i]);
1442 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1443 TRUE, &uzbl.comm.sync_stdout);
1449 parse_command(const char *cmd, const char *param, GString *result) {
1452 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1454 gchar **par = split_quoted(param, TRUE);
1455 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1457 if (c->no_split) { /* don't split */
1458 sharg_append(a, param);
1460 for (i = 0; i < g_strv_length(par); i++)
1461 sharg_append(a, par[i]);
1464 if (result == NULL) {
1465 GString *result_print = g_string_new("");
1467 c->function(uzbl.gui.web_view, a, result_print);
1468 if (result_print->len)
1469 printf("%*s\n", result_print->len, result_print->str);
1471 g_string_free(result_print, TRUE);
1473 c->function(uzbl.gui.web_view, a, result);
1476 g_array_free (a, TRUE);
1479 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1486 if(*uzbl.net.proxy_url == ' '
1487 || uzbl.net.proxy_url == NULL) {
1488 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1489 (GType) SOUP_SESSION_PROXY_URI);
1492 suri = soup_uri_new(uzbl.net.proxy_url);
1493 g_object_set(G_OBJECT(uzbl.net.soup_session),
1494 SOUP_SESSION_PROXY_URI,
1496 soup_uri_free(suri);
1503 if(file_exists(uzbl.gui.icon)) {
1504 if (uzbl.gui.main_window)
1505 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1507 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1509 g_free (uzbl.gui.icon);
1514 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1515 g_array_append_val (a, uzbl.state.uri);
1516 load_uri(uzbl.gui.web_view, a, NULL);
1517 g_array_free (a, TRUE);
1521 cmd_always_insert_mode() {
1522 uzbl.behave.insert_mode =
1523 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1529 g_object_set(G_OBJECT(uzbl.net.soup_session),
1530 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1534 cmd_max_conns_host() {
1535 g_object_set(G_OBJECT(uzbl.net.soup_session),
1536 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1541 soup_session_remove_feature
1542 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1543 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1544 /*g_free(uzbl.net.soup_logger);*/
1546 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1547 soup_session_add_feature(uzbl.net.soup_session,
1548 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1551 static WebKitWebSettings*
1553 return webkit_web_view_get_settings(uzbl.gui.web_view);
1558 WebKitWebSettings *ws = view_settings();
1559 if (uzbl.behave.font_size > 0) {
1560 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1563 if (uzbl.behave.monospace_size > 0) {
1564 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1565 uzbl.behave.monospace_size, NULL);
1567 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1568 uzbl.behave.font_size, NULL);
1574 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1578 cmd_disable_plugins() {
1579 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1580 !uzbl.behave.disable_plugins, NULL);
1584 cmd_disable_scripts() {
1585 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1586 !uzbl.behave.disable_scripts, NULL);
1590 cmd_minimum_font_size() {
1591 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1592 uzbl.behave.minimum_font_size, NULL);
1595 cmd_autoload_img() {
1596 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1597 uzbl.behave.autoload_img, NULL);
1602 cmd_autoshrink_img() {
1603 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1604 uzbl.behave.autoshrink_img, NULL);
1609 cmd_enable_spellcheck() {
1610 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1611 uzbl.behave.enable_spellcheck, NULL);
1615 cmd_enable_private() {
1616 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1617 uzbl.behave.enable_private, NULL);
1622 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1623 uzbl.behave.print_bg, NULL);
1628 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1629 uzbl.behave.style_uri, NULL);
1633 cmd_resizable_txt() {
1634 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1635 uzbl.behave.resizable_txt, NULL);
1639 cmd_default_encoding() {
1640 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1641 uzbl.behave.default_encoding, NULL);
1645 cmd_enforce_96dpi() {
1646 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1647 uzbl.behave.enforce_96dpi, NULL);
1651 cmd_caret_browsing() {
1652 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1653 uzbl.behave.caret_browsing, NULL);
1657 cmd_cookie_handler() {
1658 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1659 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1660 if ((g_strcmp0(split[0], "sh") == 0) ||
1661 (g_strcmp0(split[0], "spawn") == 0)) {
1662 g_free (uzbl.behave.cookie_handler);
1663 uzbl.behave.cookie_handler =
1664 g_strdup_printf("sync_%s %s", split[0], split[1]);
1671 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1676 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1681 if(uzbl.behave.inject_html) {
1682 webkit_web_view_load_html_string (uzbl.gui.web_view,
1683 uzbl.behave.inject_html, NULL);
1692 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1693 uzbl.behave.modmask = 0;
1695 if(uzbl.behave.modkey)
1696 g_free(uzbl.behave.modkey);
1697 uzbl.behave.modkey = buf;
1699 for (i = 0; modkeys[i].key != NULL; i++) {
1700 if (g_strrstr(buf, modkeys[i].key))
1701 uzbl.behave.modmask |= modkeys[i].mask;
1707 if (*uzbl.net.useragent == ' ') {
1708 g_free (uzbl.net.useragent);
1709 uzbl.net.useragent = NULL;
1711 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1713 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1714 g_free(uzbl.net.useragent);
1715 uzbl.net.useragent = ua;
1721 gtk_widget_ref(uzbl.gui.scrolled_win);
1722 gtk_widget_ref(uzbl.gui.mainbar);
1723 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1724 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1726 if(uzbl.behave.status_top) {
1727 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1728 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1731 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1732 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1734 gtk_widget_unref(uzbl.gui.scrolled_win);
1735 gtk_widget_unref(uzbl.gui.mainbar);
1736 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1741 set_var_value(gchar *name, gchar *val) {
1742 uzbl_cmdprop *c = NULL;
1746 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1747 /* check for the variable type */
1748 if (c->type == TYPE_STR) {
1749 buf = expand(val, 0);
1752 } else if(c->type == TYPE_INT) {
1753 int *ip = (int *)c->ptr;
1754 buf = expand(val, 0);
1755 *ip = (int)strtoul(buf, &endp, 10);
1757 } else if (c->type == TYPE_FLOAT) {
1758 float *fp = (float *)c->ptr;
1759 buf = expand(val, 0);
1760 *fp = strtod(buf, &endp);
1764 /* invoke a command specific function */
1765 if(c->func) c->func();
1772 Behaviour *b = &uzbl.behave;
1774 if(b->html_buffer->str) {
1775 webkit_web_view_load_html_string (uzbl.gui.web_view,
1776 b->html_buffer->str, b->base_url);
1777 g_string_free(b->html_buffer, TRUE);
1778 b->html_buffer = g_string_new("");
1782 enum {M_CMD, M_HTML};
1784 parse_cmd_line(const char *ctl_line, GString *result) {
1785 Behaviour *b = &uzbl.behave;
1788 if(b->mode == M_HTML) {
1789 len = strlen(b->html_endmarker);
1790 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1791 if(len == strlen(ctl_line)-1 &&
1792 !strncmp(b->html_endmarker, ctl_line, len)) {
1794 set_var_value("mode", "0");
1799 set_timeout(b->html_timeout);
1800 g_string_append(b->html_buffer, ctl_line);
1803 else if((ctl_line[0] == '#') /* Comments */
1804 || (ctl_line[0] == ' ')
1805 || (ctl_line[0] == '\n'))
1806 ; /* ignore these lines */
1807 else { /* parse a command */
1809 gchar **tokens = NULL;
1810 len = strlen(ctl_line);
1812 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1813 ctlstrip = g_strndup(ctl_line, len - 1);
1814 else ctlstrip = g_strdup(ctl_line);
1816 tokens = g_strsplit(ctlstrip, " ", 2);
1817 parse_command(tokens[0], tokens[1], result);
1824 build_stream_name(int type, const gchar* dir) {
1825 char *xwin_str = NULL;
1826 State *s = &uzbl.state;
1829 xwin_str = itos((int)uzbl.xwin);
1831 str = g_strdup_printf
1832 ("%s/uzbl_fifo_%s", dir,
1833 s->instance_name ? s->instance_name : xwin_str);
1834 } else if (type == SOCKET) {
1835 str = g_strdup_printf
1836 ("%s/uzbl_socket_%s", dir,
1837 s->instance_name ? s->instance_name : xwin_str );
1844 control_fifo(GIOChannel *gio, GIOCondition condition) {
1845 if (uzbl.state.verbose)
1846 printf("triggered\n");
1851 if (condition & G_IO_HUP)
1852 g_error ("Fifo: Read end of pipe died!\n");
1855 g_error ("Fifo: GIOChannel broke\n");
1857 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1858 if (ret == G_IO_STATUS_ERROR) {
1859 g_error ("Fifo: Error reading: %s\n", err->message);
1863 parse_cmd_line(ctl_line, NULL);
1870 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1871 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1872 if (unlink(uzbl.comm.fifo_path) == -1)
1873 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1874 g_free(uzbl.comm.fifo_path);
1875 uzbl.comm.fifo_path = NULL;
1878 if (*dir == ' ') { /* space unsets the variable */
1883 GIOChannel *chan = NULL;
1884 GError *error = NULL;
1885 gchar *path = build_stream_name(FIFO, dir);
1887 if (!file_exists(path)) {
1888 if (mkfifo (path, 0666) == 0) {
1889 // 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.
1890 chan = g_io_channel_new_file(path, "r+", &error);
1892 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1893 if (uzbl.state.verbose)
1894 printf ("init_fifo: created successfully as %s\n", path);
1895 uzbl.comm.fifo_path = path;
1897 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1898 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1899 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1900 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1902 /* if we got this far, there was an error; cleanup */
1903 if (error) g_error_free (error);
1910 control_stdin(GIOChannel *gio, GIOCondition condition) {
1912 gchar *ctl_line = NULL;
1915 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1916 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1919 parse_cmd_line(ctl_line, NULL);
1927 GIOChannel *chan = NULL;
1928 GError *error = NULL;
1930 chan = g_io_channel_unix_new(fileno(stdin));
1932 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1933 g_error ("Stdin: could not add watch\n");
1935 if (uzbl.state.verbose)
1936 printf ("Stdin: watch added successfully\n");
1939 g_error ("Stdin: Error while opening: %s\n", error->message);
1941 if (error) g_error_free (error);
1945 control_socket(GIOChannel *chan) {
1946 struct sockaddr_un remote;
1947 unsigned int t = sizeof(remote);
1949 GIOChannel *clientchan;
1951 clientsock = accept (g_io_channel_unix_get_fd(chan),
1952 (struct sockaddr *) &remote, &t);
1954 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1955 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1956 (GIOFunc) control_client_socket, clientchan);
1963 control_client_socket(GIOChannel *clientchan) {
1965 GString *result = g_string_new("");
1966 GError *error = NULL;
1970 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1971 if (ret == G_IO_STATUS_ERROR) {
1972 g_warning ("Error reading: %s\n", error->message);
1973 g_io_channel_shutdown(clientchan, TRUE, &error);
1975 } else if (ret == G_IO_STATUS_EOF) {
1976 /* shutdown and remove channel watch from main loop */
1977 g_io_channel_shutdown(clientchan, TRUE, &error);
1982 parse_cmd_line (ctl_line, result);
1983 g_string_append_c(result, '\n');
1984 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1986 if (ret == G_IO_STATUS_ERROR) {
1987 g_warning ("Error writing: %s", error->message);
1989 g_io_channel_flush(clientchan, &error);
1992 if (error) g_error_free (error);
1993 g_string_free(result, TRUE);
1999 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2000 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2001 if (unlink(uzbl.comm.socket_path) == -1)
2002 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2003 g_free(uzbl.comm.socket_path);
2004 uzbl.comm.socket_path = NULL;
2012 GIOChannel *chan = NULL;
2014 struct sockaddr_un local;
2015 gchar *path = build_stream_name(SOCKET, dir);
2017 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2019 local.sun_family = AF_UNIX;
2020 strcpy (local.sun_path, path);
2021 unlink (local.sun_path);
2023 len = strlen (local.sun_path) + sizeof (local.sun_family);
2024 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2025 if (uzbl.state.verbose)
2026 printf ("init_socket: opened in %s\n", path);
2029 if( (chan = g_io_channel_unix_new(sock)) ) {
2030 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2031 uzbl.comm.socket_path = path;
2034 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2036 /* if we got this far, there was an error; cleanup */
2043 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2044 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2046 // this function may be called very early when the templates are not set (yet), hence the checks
2048 update_title (void) {
2049 Behaviour *b = &uzbl.behave;
2052 if (b->show_status) {
2053 if (b->title_format_short) {
2054 parsed = expand_template(b->title_format_short, FALSE);
2055 if (uzbl.gui.main_window)
2056 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2059 if (b->status_format) {
2060 parsed = expand_template(b->status_format, TRUE);
2061 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2064 if (b->status_background) {
2066 gdk_color_parse (b->status_background, &color);
2067 //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)
2068 if (uzbl.gui.main_window)
2069 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2072 if (b->title_format_long) {
2073 parsed = expand_template(b->title_format_long, FALSE);
2074 if (uzbl.gui.main_window)
2075 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2082 key_press_cb (GtkWidget* window, GdkEventKey* event)
2084 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2088 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2089 || 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)
2092 /* turn off insert mode (if always_insert_mode is not used) */
2093 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2094 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2099 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2102 if (event->keyval == GDK_Escape) {
2103 g_string_truncate(uzbl.state.keycmd, 0);
2105 dehilight(uzbl.gui.web_view, NULL, NULL);
2109 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2110 if (event->keyval == GDK_Insert) {
2112 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2113 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2115 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2118 g_string_append (uzbl.state.keycmd, str);
2125 if (event->keyval == GDK_BackSpace)
2126 keycmd_bs(NULL, NULL, NULL);
2128 gboolean key_ret = FALSE;
2129 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2131 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2133 run_keycmd(key_ret);
2135 if (key_ret) return (!uzbl.behave.insert_mode);
2140 run_keycmd(const gboolean key_ret) {
2141 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2143 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2144 g_string_truncate(uzbl.state.keycmd, 0);
2145 parse_command(act->name, act->param, NULL);
2149 /* try if it's an incremental keycmd or one that takes args, and run it */
2150 GString* short_keys = g_string_new ("");
2151 GString* short_keys_inc = g_string_new ("");
2153 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2154 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2155 g_string_assign(short_keys_inc, short_keys->str);
2156 g_string_append_c(short_keys, '_');
2157 g_string_append_c(short_keys_inc, '*');
2159 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2160 /* run normal cmds only if return was pressed */
2161 exec_paramcmd(act, i);
2162 g_string_truncate(uzbl.state.keycmd, 0);
2164 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2165 if (key_ret) /* just quit the incremental command on return */
2166 g_string_truncate(uzbl.state.keycmd, 0);
2167 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2171 g_string_truncate(short_keys, short_keys->len - 1);
2173 g_string_free (short_keys, TRUE);
2174 g_string_free (short_keys_inc, TRUE);
2178 exec_paramcmd(const Action *act, const guint i) {
2179 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2180 GString *actionname = g_string_new ("");
2181 GString *actionparam = g_string_new ("");
2182 g_string_erase (parampart, 0, i+1);
2184 g_string_printf (actionname, act->name, parampart->str);
2186 g_string_printf (actionparam, act->param, parampart->str);
2187 parse_command(actionname->str, actionparam->str, NULL);
2188 g_string_free(actionname, TRUE);
2189 g_string_free(actionparam, TRUE);
2190 g_string_free(parampart, TRUE);
2198 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2199 //main_window_ref = g_object_ref(scrolled_window);
2200 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
2202 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2203 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2205 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2206 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2207 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2208 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2209 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2210 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2211 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2212 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2213 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2214 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2215 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2217 return scrolled_window;
2224 g->mainbar = gtk_hbox_new (FALSE, 0);
2226 /* keep a reference to the bar so we can re-pack it at runtime*/
2227 //sbar_ref = g_object_ref(g->mainbar);
2229 g->mainbar_label = gtk_label_new ("");
2230 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2231 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2232 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2233 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2234 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2235 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2240 GtkWidget* create_window () {
2241 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2242 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2243 gtk_widget_set_name (window, "Uzbl browser");
2244 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2245 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2251 GtkPlug* create_plug () {
2252 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2253 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2254 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2261 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2263 If actname is one that calls an external command, this function will inject
2264 newargs in front of the user-provided args in that command line. They will
2265 come become after the body of the script (in sh) or after the name of
2266 the command to execute (in spawn).
2267 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2268 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2270 The return value consist of two strings: the action (sh, ...) and its args.
2272 If act is not one that calls an external command, then the given action merely
2275 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2276 gchar *actdup = g_strdup(actname);
2277 g_array_append_val(rets, actdup);
2279 if ((g_strcmp0(actname, "spawn") == 0) ||
2280 (g_strcmp0(actname, "sh") == 0) ||
2281 (g_strcmp0(actname, "sync_spawn") == 0) ||
2282 (g_strcmp0(actname, "sync_sh") == 0)) {
2284 GString *a = g_string_new("");
2285 gchar **spawnparts = split_quoted(origargs, FALSE);
2286 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2287 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2289 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2290 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2292 g_array_append_val(rets, a->str);
2293 g_string_free(a, FALSE);
2294 g_strfreev(spawnparts);
2296 gchar *origdup = g_strdup(origargs);
2297 g_array_append_val(rets, origdup);
2299 return (gchar**)g_array_free(rets, FALSE);
2303 run_handler (const gchar *act, const gchar *args) {
2304 /* Consider this code a temporary hack to make the handlers usable.
2305 In practice, all this splicing, injection, and reconstruction is
2306 inefficient, annoying and hard to manage. Potential pitfalls arise
2307 when the handler specific args 1) are not quoted (the handler
2308 callbacks should take care of this) 2) are quoted but interfere
2309 with the users' own quotation. A more ideal solution is
2310 to refactor parse_command so that it doesn't just take a string
2311 and execute it; rather than that, we should have a function which
2312 returns the argument vector parsed from the string. This vector
2313 could be modified (e.g. insert additional args into it) before
2314 passing it to the next function that actually executes it. Though
2315 it still isn't perfect for chain actions.. will reconsider & re-
2316 factor when I have the time. -duc */
2318 char **parts = g_strsplit(act, " ", 2);
2320 if (g_strcmp0(parts[0], "chain") == 0) {
2321 GString *newargs = g_string_new("");
2322 gchar **chainparts = split_quoted(parts[1], FALSE);
2324 /* for every argument in the chain, inject the handler args
2325 and make sure the new parts are wrapped in quotes */
2326 gchar **cp = chainparts;
2328 gchar *quotless = NULL;
2329 gchar **spliced_quotless = NULL; // sigh -_-;
2330 gchar **inpart = NULL;
2333 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2335 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2336 } else quotless = g_strdup(*cp);
2338 spliced_quotless = g_strsplit(quotless, " ", 2);
2339 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2340 g_strfreev(spliced_quotless);
2342 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2348 parse_command(parts[0], &(newargs->str[1]), NULL);
2349 g_string_free(newargs, TRUE);
2350 g_strfreev(chainparts);
2353 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2354 parse_command(inparts[0], inparts[1], NULL);
2362 add_binding (const gchar *key, const gchar *act) {
2363 char **parts = g_strsplit(act, " ", 2);
2370 if (uzbl.state.verbose)
2371 printf ("Binding %-10s : %s\n", key, act);
2372 action = new_action(parts[0], parts[1]);
2374 if (g_hash_table_remove (uzbl.bindings, key))
2375 g_warning ("Overwriting existing binding for \"%s\"", key);
2376 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2381 get_xdg_var (XDG_Var xdg) {
2382 const gchar* actual_value = getenv (xdg.environmental);
2383 const gchar* home = getenv ("HOME");
2384 gchar* return_value;
2386 if (! actual_value || strcmp (actual_value, "") == 0) {
2387 if (xdg.default_value) {
2388 return_value = str_replace ("~", home, xdg.default_value);
2390 return_value = NULL;
2393 return_value = str_replace("~", home, actual_value);
2396 return return_value;
2400 find_xdg_file (int xdg_type, char* filename) {
2401 /* xdg_type = 0 => config
2402 xdg_type = 1 => data
2403 xdg_type = 2 => cache*/
2405 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2406 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2409 gchar* temporary_string;
2413 if (! file_exists (temporary_file) && xdg_type != 2) {
2414 buf = get_xdg_var (XDG[3 + xdg_type]);
2415 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2418 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2419 g_free (temporary_file);
2420 temporary_file = g_strconcat (temporary_string, filename, NULL);
2424 //g_free (temporary_string); - segfaults.
2426 if (file_exists (temporary_file)) {
2427 return temporary_file;
2434 State *s = &uzbl.state;
2435 Network *n = &uzbl.net;
2437 for (i = 0; default_config[i].command != NULL; i++) {
2438 parse_cmd_line(default_config[i].command, NULL);
2441 if (g_strcmp0(s->config_file, "-") == 0) {
2442 s->config_file = NULL;
2446 if (!s->config_file) {
2447 s->config_file = find_xdg_file (0, "/uzbl/config");
2450 if (s->config_file) {
2451 GArray* lines = read_file_by_line (s->config_file);
2455 while ((line = g_array_index(lines, gchar*, i))) {
2456 parse_cmd_line (line, NULL);
2460 g_array_free (lines, TRUE);
2462 if (uzbl.state.verbose)
2463 printf ("No configuration file loaded.\n");
2466 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2469 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2472 if (!uzbl.behave.cookie_handler)
2475 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2476 GString *s = g_string_new ("");
2477 SoupURI * soup_uri = soup_message_get_uri(msg);
2478 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2479 run_handler(uzbl.behave.cookie_handler, s->str);
2481 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2482 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2483 if ( p != NULL ) *p = '\0';
2484 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2486 if (uzbl.comm.sync_stdout)
2487 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2489 g_string_free(s, TRUE);
2493 save_cookies (SoupMessage *msg, gpointer user_data){
2497 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2498 cookie = soup_cookie_to_set_cookie_header(ck->data);
2499 SoupURI * soup_uri = soup_message_get_uri(msg);
2500 GString *s = g_string_new ("");
2501 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2502 run_handler(uzbl.behave.cookie_handler, s->str);
2504 g_string_free(s, TRUE);
2509 /* --- WEBINSPECTOR --- */
2511 hide_window_cb(GtkWidget *widget, gpointer data) {
2514 gtk_widget_hide(widget);
2517 static WebKitWebView*
2518 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2521 (void) web_inspector;
2522 GtkWidget* scrolled_window;
2523 GtkWidget* new_web_view;
2526 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2527 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2528 G_CALLBACK(hide_window_cb), NULL);
2530 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2531 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2532 gtk_widget_show(g->inspector_window);
2534 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2535 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2536 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2537 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2538 gtk_widget_show(scrolled_window);
2540 new_web_view = webkit_web_view_new();
2541 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2543 return WEBKIT_WEB_VIEW(new_web_view);
2547 inspector_show_window_cb (WebKitWebInspector* inspector){
2549 gtk_widget_show(uzbl.gui.inspector_window);
2553 /* TODO: Add variables and code to make use of these functions */
2555 inspector_close_window_cb (WebKitWebInspector* inspector){
2561 inspector_attach_window_cb (WebKitWebInspector* inspector){
2567 inspector_detach_window_cb (WebKitWebInspector* inspector){
2573 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2579 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2585 set_up_inspector() {
2587 WebKitWebSettings *settings = view_settings();
2588 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2590 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2591 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2592 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2593 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2594 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2595 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2596 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2598 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2602 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2604 uzbl_cmdprop *c = v;
2609 if(c->type == TYPE_STR)
2610 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2611 else if(c->type == TYPE_INT)
2612 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2616 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2620 printf("bind %s = %s %s\n", (char *)k ,
2621 (char *)a->name, a->param?(char *)a->param:"");
2626 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2627 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2632 main (int argc, char* argv[]) {
2633 gtk_init (&argc, &argv);
2634 if (!g_thread_supported ())
2635 g_thread_init (NULL);
2636 uzbl.state.executable_path = g_strdup(argv[0]);
2637 uzbl.state.selected_url = NULL;
2638 uzbl.state.searchtx = NULL;
2640 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2641 g_option_context_add_main_entries (context, entries, NULL);
2642 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2643 g_option_context_parse (context, &argc, &argv, NULL);
2644 g_option_context_free(context);
2646 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2647 gboolean verbose_override = uzbl.state.verbose;
2649 /* initialize hash table */
2650 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2652 uzbl.net.soup_session = webkit_get_default_session();
2653 uzbl.state.keycmd = g_string_new("");
2655 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2656 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2657 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2658 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2659 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2660 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2663 if(uname(&uzbl.state.unameinfo) == -1)
2664 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2666 uzbl.gui.sbar.progress_s = g_strdup("=");
2667 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2668 uzbl.gui.sbar.progress_w = 10;
2670 /* HTML mode defaults*/
2671 uzbl.behave.html_buffer = g_string_new("");
2672 uzbl.behave.html_endmarker = g_strdup(".");
2673 uzbl.behave.html_timeout = 60;
2674 uzbl.behave.base_url = g_strdup("http://invalid");
2676 /* default mode indicators */
2677 uzbl.behave.insert_indicator = g_strdup("I");
2678 uzbl.behave.cmd_indicator = g_strdup("C");
2682 make_var_to_name_hash();
2684 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2686 uzbl.gui.scrolled_win = create_browser();
2689 /* initial packing */
2690 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2691 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2693 if (uzbl.state.socket_id) {
2694 uzbl.gui.plug = create_plug ();
2695 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2696 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2698 uzbl.gui.main_window = create_window ();
2699 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2700 gtk_widget_show_all (uzbl.gui.main_window);
2701 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2704 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2706 if (uzbl.state.verbose) {
2707 printf("Uzbl start location: %s\n", argv[0]);
2708 if (uzbl.state.socket_id)
2709 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2711 printf("window_id %i\n",(int) uzbl.xwin);
2712 printf("pid %i\n", getpid ());
2713 printf("name: %s\n", uzbl.state.instance_name);
2716 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2717 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2718 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2719 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2720 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2724 if (!uzbl.behave.show_status)
2725 gtk_widget_hide(uzbl.gui.mainbar);
2732 if (verbose_override > uzbl.state.verbose)
2733 uzbl.state.verbose = verbose_override;
2736 set_var_value("uri", uri_override);
2737 g_free(uri_override);
2738 } else if (uzbl.state.uri)
2739 cmd_load_uri(uzbl.gui.web_view, NULL);
2744 return EXIT_SUCCESS;
2747 /* vi: set et ts=4: */