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>
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
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};
198 get_exp_type(gchar *s) {
202 else if(*(s+1) == '{')
203 return EXP_BRACED_VAR;
205 return EXP_SIMPLE_VAR;
211 expand_vars(char *s) {
214 char ret[4096], *vend;
215 GString *buf = g_string_new("");
218 gchar *cmd_stdout = NULL;
224 g_string_append_c(buf, *++s);
228 etype = get_exp_type(s);
243 if( (vend = strchr(s, upto)) ||
244 (vend = strchr(s, '\0')) ) {
245 strncpy(ret, s, vend-s);
249 if(etype == EXP_SIMPLE_VAR || etype == EXP_BRACED_VAR) {
250 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
251 if(c->type == TYPE_STR)
252 g_string_append(buf, (gchar *)*c->ptr);
253 else if(c->type == TYPE_INT) {
254 char *b = itos((int)*c->ptr);
255 g_string_append(buf, b);
259 if(upto == ' ') s = vend;
262 else if(etype == EXP_EXPR) {
263 mycmd = expand_vars(ret);
264 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
268 g_printerr("error on running command: %s\n", err->message);
271 else if (*cmd_stdout) {
272 g_string_append(buf, cmd_stdout);
279 g_string_append_c(buf, *s);
284 return g_string_free(buf, FALSE);
291 snprintf(tmp, sizeof(tmp), "%i", val);
292 return g_strdup(tmp);
296 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
299 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
302 str_replace (const char* search, const char* replace, const char* string) {
306 buf = g_strsplit (string, search, -1);
307 ret = g_strjoinv (replace, buf);
308 g_strfreev(buf); // somebody said this segfaults
314 read_file_by_line (gchar *path) {
315 GIOChannel *chan = NULL;
316 gchar *readbuf = NULL;
318 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
321 chan = g_io_channel_new_file(path, "r", NULL);
324 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
325 const gchar* val = g_strdup (readbuf);
326 g_array_append_val (lines, val);
331 g_io_channel_unref (chan);
333 fprintf(stderr, "File '%s' not be read.\n", path);
340 gchar* parseenv (char* string) {
341 extern char** environ;
342 gchar* tmpstr = NULL;
346 while (environ[i] != NULL) {
347 gchar** env = g_strsplit (environ[i], "=", 2);
348 gchar* envname = g_strconcat ("$", env[0], NULL);
350 if (g_strrstr (string, envname) != NULL) {
351 tmpstr = g_strdup(string);
353 string = str_replace(envname, env[1], tmpstr);
358 g_strfreev (env); // somebody said this breaks uzbl
366 setup_signal(int signr, sigfunc *shandler) {
367 struct sigaction nh, oh;
369 nh.sa_handler = shandler;
370 sigemptyset(&nh.sa_mask);
373 if(sigaction(signr, &nh, &oh) < 0)
381 if (uzbl.behave.fifo_dir)
382 unlink (uzbl.comm.fifo_path);
383 if (uzbl.behave.socket_dir)
384 unlink (uzbl.comm.socket_path);
386 g_free(uzbl.state.executable_path);
387 g_string_free(uzbl.state.keycmd, TRUE);
388 g_hash_table_destroy(uzbl.bindings);
389 g_hash_table_destroy(uzbl.behave.commands);
392 /* used for html_mode_timeout
393 * be sure to extend this function to use
394 * more timers if needed in other places
397 set_timeout(int seconds) {
399 memset(&t, 0, sizeof t);
401 t.it_value.tv_sec = seconds;
402 t.it_value.tv_usec = 0;
403 setitimer(ITIMER_REAL, &t, NULL);
406 /* --- SIGNAL HANDLER --- */
409 catch_sigterm(int s) {
415 catch_sigint(int s) {
425 set_var_value("mode", "0");
430 /* --- CALLBACKS --- */
433 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
436 (void) navigation_action;
437 (void) policy_decision;
439 const gchar* uri = webkit_network_request_get_uri (request);
440 if (uzbl.state.verbose)
441 printf("New window requested -> %s \n", uri);
442 new_window_load_uri(uri);
447 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
452 /* If we can display it, let's display it... */
453 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
454 webkit_web_policy_decision_use (policy_decision);
458 /* ...everything we can't displayed is downloaded */
459 webkit_web_policy_decision_download (policy_decision);
464 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
468 if (uzbl.state.selected_url != NULL) {
469 if (uzbl.state.verbose)
470 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
471 new_window_load_uri(uzbl.state.selected_url);
473 if (uzbl.state.verbose)
474 printf("New web view -> %s\n","Nothing to open, exiting");
480 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
483 if (uzbl.behave.download_handler) {
484 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
485 if (uzbl.state.verbose)
486 printf("Download -> %s\n",uri);
487 /* if urls not escaped, we may have to escape and quote uri before this call */
488 run_handler(uzbl.behave.download_handler, uri);
493 /* scroll a bar in a given direction */
495 scroll (GtkAdjustment* bar, GArray *argv) {
499 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
500 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
501 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
505 scroll_begin(WebKitWebView* page, GArray *argv) {
506 (void) page; (void) argv;
507 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
511 scroll_end(WebKitWebView* page, GArray *argv) {
512 (void) page; (void) argv;
513 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
514 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
518 scroll_vert(WebKitWebView* page, GArray *argv) {
520 scroll(uzbl.gui.bar_v, argv);
524 scroll_horz(WebKitWebView* page, GArray *argv) {
526 scroll(uzbl.gui.bar_h, argv);
531 if (!uzbl.behave.show_status) {
532 gtk_widget_hide(uzbl.gui.mainbar);
534 gtk_widget_show(uzbl.gui.mainbar);
540 toggle_zoom_type (WebKitWebView* page, GArray *argv) {
544 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
548 toggle_status_cb (WebKitWebView* page, GArray *argv) {
552 if (uzbl.behave.show_status) {
553 gtk_widget_hide(uzbl.gui.mainbar);
555 gtk_widget_show(uzbl.gui.mainbar);
557 uzbl.behave.show_status = !uzbl.behave.show_status;
562 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
566 //Set selected_url state variable
567 g_free(uzbl.state.selected_url);
568 uzbl.state.selected_url = NULL;
570 uzbl.state.selected_url = g_strdup(link);
576 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
580 if (uzbl.gui.main_title)
581 g_free (uzbl.gui.main_title);
582 uzbl.gui.main_title = g_strdup (title);
587 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
590 uzbl.gui.sbar.load_progress = progress;
595 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
599 if (uzbl.behave.load_finish_handler)
600 run_handler(uzbl.behave.load_finish_handler, "");
604 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
608 uzbl.gui.sbar.load_progress = 0;
609 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
610 if (uzbl.behave.load_start_handler)
611 run_handler(uzbl.behave.load_start_handler, "");
615 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
618 g_free (uzbl.state.uri);
619 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
620 uzbl.state.uri = g_string_free (newuri, FALSE);
621 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
622 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
625 if (uzbl.behave.load_commit_handler)
626 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
630 destroy_cb (GtkWidget* widget, gpointer data) {
638 if (uzbl.behave.history_handler) {
640 struct tm * timeinfo;
643 timeinfo = localtime ( &rawtime );
644 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
645 run_handler(uzbl.behave.history_handler, date);
650 /* VIEW funcs (little webkit wrappers) */
651 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
653 VIEWFUNC(reload_bypass_cache)
654 VIEWFUNC(stop_loading)
661 /* -- command to callback/function map for things we cannot attach to any signals */
662 static struct {char *name; Command command[2];} cmdlist[] =
663 { /* key function no_split */
664 { "back", {view_go_back, 0} },
665 { "forward", {view_go_forward, 0} },
666 { "scroll_vert", {scroll_vert, 0} },
667 { "scroll_horz", {scroll_horz, 0} },
668 { "scroll_begin", {scroll_begin, 0} },
669 { "scroll_end", {scroll_end, 0} },
670 { "reload", {view_reload, 0}, },
671 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
672 { "stop", {view_stop_loading, 0}, },
673 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
674 { "zoom_out", {view_zoom_out, 0}, },
675 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
676 { "uri", {load_uri, NOSPLIT} },
677 { "js", {run_js, NOSPLIT} },
678 { "script", {run_external_js, 0} },
679 { "toggle_status", {toggle_status_cb, 0} },
680 { "spawn", {spawn, 0} },
681 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
682 { "sh", {spawn_sh, 0} },
683 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
684 { "exit", {close_uzbl, 0} },
685 { "search", {search_forward_text, NOSPLIT} },
686 { "search_reverse", {search_reverse_text, NOSPLIT} },
687 { "dehilight", {dehilight, 0} },
688 { "toggle_insert_mode", {toggle_insert_mode, 0} },
689 { "set", {set_var, NOSPLIT} },
690 //{ "get", {get_var, NOSPLIT} },
691 { "bind", {act_bind, NOSPLIT} },
692 { "dump_config", {act_dump_config, 0} },
693 { "keycmd", {keycmd, NOSPLIT} },
694 { "keycmd_nl", {keycmd_nl, NOSPLIT} },
695 { "keycmd_bs", {keycmd_bs, 0} },
696 { "chain", {chain, 0} },
697 { "print", {print, NOSPLIT} }
704 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
706 for (i = 0; i < LENGTH(cmdlist); i++)
707 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
710 /* -- CORE FUNCTIONS -- */
713 free_action(gpointer act) {
714 Action *action = (Action*)act;
715 g_free(action->name);
717 g_free(action->param);
722 new_action(const gchar *name, const gchar *param) {
723 Action *action = g_new(Action, 1);
725 action->name = g_strdup(name);
727 action->param = g_strdup(param);
729 action->param = NULL;
735 file_exists (const char * filename) {
736 return (access(filename, F_OK) == 0);
740 set_var(WebKitWebView *page, GArray *argv) {
742 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
743 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
744 set_var_value(g_strstrip(split[0]), value);
750 print(WebKitWebView *page, GArray *argv) {
754 buf = expand_vars(argv_idx(argv, 0));
760 act_bind(WebKitWebView *page, GArray *argv) {
762 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
763 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
764 add_binding(g_strstrip(split[0]), value);
776 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
779 if (argv_idx(argv, 0)) {
780 if (strcmp (argv_idx(argv, 0), "0") == 0) {
781 uzbl.behave.insert_mode = FALSE;
783 uzbl.behave.insert_mode = TRUE;
786 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
793 load_uri (WebKitWebView *web_view, GArray *argv) {
794 if (argv_idx(argv, 0)) {
795 GString* newuri = g_string_new (argv_idx(argv, 0));
796 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
797 run_js(web_view, argv);
800 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
801 g_string_prepend (newuri, "http://");
802 /* if we do handle cookies, ask our handler for them */
803 webkit_web_view_load_uri (web_view, newuri->str);
804 g_string_free (newuri, TRUE);
809 run_js (WebKitWebView * web_view, GArray *argv) {
810 if (argv_idx(argv, 0))
811 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
815 run_external_js (WebKitWebView * web_view, GArray *argv) {
816 if (argv_idx(argv, 0)) {
817 GArray* lines = read_file_by_line (argv_idx (argv, 0));
822 while ((line = g_array_index(lines, gchar*, i))) {
824 js = g_strdup (line);
826 gchar* newjs = g_strconcat (js, line, NULL);
833 if (uzbl.state.verbose)
834 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
836 if (argv_idx (argv, 1)) {
837 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
841 webkit_web_view_execute_script (web_view, js);
843 g_array_free (lines, TRUE);
848 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
849 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
850 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
851 webkit_web_view_unmark_text_matches (page);
852 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
853 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
857 if (uzbl.state.searchtx) {
858 if (uzbl.state.verbose)
859 printf ("Searching: %s\n", uzbl.state.searchtx);
860 webkit_web_view_set_highlight_text_matches (page, TRUE);
861 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
866 search_forward_text (WebKitWebView *page, GArray *argv) {
867 search_text(page, argv, TRUE);
871 search_reverse_text (WebKitWebView *page, GArray *argv) {
872 search_text(page, argv, FALSE);
876 dehilight (WebKitWebView *page, GArray *argv) {
878 webkit_web_view_set_highlight_text_matches (page, FALSE);
883 new_window_load_uri (const gchar * uri) {
884 GString* to_execute = g_string_new ("");
885 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
887 for (i = 0; entries[i].long_name != NULL; i++) {
888 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
889 gchar** str = (gchar**)entries[i].arg_data;
891 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
895 if (uzbl.state.verbose)
896 printf("\n%s\n", to_execute->str);
897 g_spawn_command_line_async (to_execute->str, NULL);
898 g_string_free (to_execute, TRUE);
902 chain (WebKitWebView *page, GArray *argv) {
905 gchar **parts = NULL;
907 while ((a = argv_idx(argv, i++))) {
908 parts = g_strsplit (a, " ", 2);
909 parse_command(parts[0], parts[1]);
915 keycmd (WebKitWebView *page, GArray *argv) {
918 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
924 keycmd_nl (WebKitWebView *page, GArray *argv) {
927 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
933 keycmd_bs (WebKitWebView *page, GArray *argv) {
937 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
939 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
944 close_uzbl (WebKitWebView *page, GArray *argv) {
950 /* --Statusbar functions-- */
952 build_progressbar_ascii(int percent) {
953 int width=uzbl.gui.sbar.progress_w;
956 GString *bar = g_string_new("");
958 l = (double)percent*((double)width/100.);
959 l = (int)(l+.5)>=(int)l ? l+.5 : l;
961 for(i=0; i<(int)l; i++)
962 g_string_append(bar, uzbl.gui.sbar.progress_s);
965 g_string_append(bar, uzbl.gui.sbar.progress_u);
967 return g_string_free(bar, FALSE);
972 const GScannerConfig scan_config = {
975 ) /* cset_skip_characters */,
980 ) /* cset_identifier_first */,
987 ) /* cset_identifier_nth */,
988 ( "" ) /* cpair_comment_single */,
990 TRUE /* case_sensitive */,
992 FALSE /* skip_comment_multi */,
993 FALSE /* skip_comment_single */,
994 FALSE /* scan_comment_multi */,
995 TRUE /* scan_identifier */,
996 TRUE /* scan_identifier_1char */,
997 FALSE /* scan_identifier_NULL */,
998 TRUE /* scan_symbols */,
999 FALSE /* scan_binary */,
1000 FALSE /* scan_octal */,
1001 FALSE /* scan_float */,
1002 FALSE /* scan_hex */,
1003 FALSE /* scan_hex_dollar */,
1004 FALSE /* scan_string_sq */,
1005 FALSE /* scan_string_dq */,
1006 TRUE /* numbers_2_int */,
1007 FALSE /* int_2_float */,
1008 FALSE /* identifier_2_string */,
1009 FALSE /* char_2_token */,
1010 FALSE /* symbol_2_token */,
1011 TRUE /* scope_0_fallback */,
1016 uzbl.scan = g_scanner_new(&scan_config);
1017 while(symp->symbol_name) {
1018 g_scanner_scope_add_symbol(uzbl.scan, 0,
1020 GINT_TO_POINTER(symp->symbol_token));
1026 expand_template(const char *template, gboolean escape_markup) {
1027 if(!template) return NULL;
1029 GTokenType token = G_TOKEN_NONE;
1030 GString *ret = g_string_new("");
1034 g_scanner_input_text(uzbl.scan, template, strlen(template));
1035 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1036 token = g_scanner_get_next_token(uzbl.scan);
1038 if(token == G_TOKEN_SYMBOL) {
1039 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1043 buf = uzbl.state.uri?
1044 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1045 g_string_append(ret, buf);
1049 g_string_append(ret, uzbl.state.uri?
1050 uzbl.state.uri:g_strdup(""));
1053 buf = itos(uzbl.gui.sbar.load_progress);
1054 g_string_append(ret, buf);
1057 case SYM_LOADPRGSBAR:
1058 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1059 g_string_append(ret, buf);
1064 buf = uzbl.gui.main_title?
1065 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1066 g_string_append(ret, buf);
1070 g_string_append(ret, uzbl.gui.main_title?
1071 uzbl.gui.main_title:g_strdup(""));
1073 case SYM_SELECTED_URI:
1075 buf = uzbl.state.selected_url?
1076 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1077 g_string_append(ret, buf);
1081 g_string_append(ret, uzbl.state.selected_url?
1082 uzbl.state.selected_url:g_strdup(""));
1085 buf = itos(uzbl.xwin);
1086 g_string_append(ret,
1087 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1092 buf = uzbl.state.keycmd->str?
1093 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1094 g_string_append(ret, buf);
1098 g_string_append(ret, uzbl.state.keycmd->str?
1099 uzbl.state.keycmd->str:g_strdup(""));
1102 g_string_append(ret,
1103 uzbl.behave.insert_mode?
1104 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1107 g_string_append(ret,
1108 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1110 /* useragent syms */
1112 buf = itos(WEBKIT_MAJOR_VERSION);
1113 g_string_append(ret, buf);
1117 buf = itos(WEBKIT_MINOR_VERSION);
1118 g_string_append(ret, buf);
1122 buf = itos(WEBKIT_MICRO_VERSION);
1123 g_string_append(ret, buf);
1127 g_string_append(ret, uzbl.state.unameinfo.sysname);
1130 g_string_append(ret, uzbl.state.unameinfo.nodename);
1133 g_string_append(ret, uzbl.state.unameinfo.release);
1136 g_string_append(ret, uzbl.state.unameinfo.version);
1139 g_string_append(ret, uzbl.state.unameinfo.machine);
1142 g_string_append(ret, ARCH);
1145 case SYM_DOMAINNAME:
1146 g_string_append(ret, uzbl.state.unameinfo.domainname);
1150 g_string_append(ret, COMMIT);
1156 else if(token == G_TOKEN_INT) {
1157 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1158 g_string_append(ret, buf);
1161 else if(token == G_TOKEN_IDENTIFIER) {
1162 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1164 else if(token == G_TOKEN_CHAR) {
1165 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1169 return g_string_free(ret, FALSE);
1171 /* --End Statusbar functions-- */
1174 sharg_append(GArray *a, const gchar *str) {
1175 const gchar *s = (str ? str : "");
1176 g_array_append_val(a, s);
1179 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1181 run_command (const gchar *command, const guint npre, const gchar **args,
1182 const gboolean sync, char **output_stdout) {
1183 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1186 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1187 gchar *pid = itos(getpid());
1188 gchar *xwin = itos(uzbl.xwin);
1190 sharg_append(a, command);
1191 for (i = 0; i < npre; i++) /* add n args before the default vars */
1192 sharg_append(a, args[i]);
1193 sharg_append(a, uzbl.state.config_file);
1194 sharg_append(a, pid);
1195 sharg_append(a, xwin);
1196 sharg_append(a, uzbl.comm.fifo_path);
1197 sharg_append(a, uzbl.comm.socket_path);
1198 sharg_append(a, uzbl.state.uri);
1199 sharg_append(a, uzbl.gui.main_title);
1201 for (i = npre; i < g_strv_length((gchar**)args); i++)
1202 sharg_append(a, args[i]);
1206 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1208 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1209 NULL, NULL, output_stdout, NULL, NULL, &err);
1210 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1211 NULL, NULL, NULL, &err);
1213 if (uzbl.state.verbose) {
1214 GString *s = g_string_new("spawned:");
1215 for (i = 0; i < (a->len); i++) {
1216 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1217 g_string_append_printf(s, " %s", qarg);
1220 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1221 printf("%s\n", s->str);
1222 g_string_free(s, TRUE);
1224 printf("Stdout: %s\n", *output_stdout);
1228 g_printerr("error on run_command: %s\n", err->message);
1233 g_array_free (a, TRUE);
1238 split_quoted(const gchar* src, const gboolean unquote) {
1239 /* split on unquoted space, return array of strings;
1240 remove a layer of quotes and backslashes if unquote */
1241 if (!src) return NULL;
1243 gboolean dq = FALSE;
1244 gboolean sq = FALSE;
1245 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1246 GString *s = g_string_new ("");
1250 for (p = src; *p != '\0'; p++) {
1251 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1252 else if (*p == '\\') { g_string_append_c(s, *p++);
1253 g_string_append_c(s, *p); }
1254 else if ((*p == '"') && unquote && !sq) dq = !dq;
1255 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1257 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1258 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1260 else if ((*p == ' ') && !dq && !sq) {
1261 dup = g_strdup(s->str);
1262 g_array_append_val(a, dup);
1263 g_string_truncate(s, 0);
1264 } else g_string_append_c(s, *p);
1266 dup = g_strdup(s->str);
1267 g_array_append_val(a, dup);
1268 ret = (gchar**)a->data;
1269 g_array_free (a, FALSE);
1270 g_string_free (s, TRUE);
1275 spawn(WebKitWebView *web_view, GArray *argv) {
1277 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1278 if (argv_idx(argv, 0))
1279 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1283 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1286 if (argv_idx(argv, 0))
1287 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1288 TRUE, &uzbl.comm.sync_stdout);
1292 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1294 if (!uzbl.behave.shell_cmd) {
1295 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1300 gchar *spacer = g_strdup("");
1301 g_array_insert_val(argv, 1, spacer);
1302 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1304 for (i = 1; i < g_strv_length(cmd); i++)
1305 g_array_prepend_val(argv, cmd[i]);
1307 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1313 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1315 if (!uzbl.behave.shell_cmd) {
1316 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1321 gchar *spacer = g_strdup("");
1322 g_array_insert_val(argv, 1, spacer);
1323 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1325 for (i = 1; i < g_strv_length(cmd); i++)
1326 g_array_prepend_val(argv, cmd[i]);
1328 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1329 TRUE, &uzbl.comm.sync_stdout);
1335 parse_command(const char *cmd, const char *param) {
1338 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1341 gchar **par = split_quoted(param, TRUE);
1342 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1344 if (c[1] == NOSPLIT) { /* don't split */
1345 sharg_append(a, param);
1347 for (i = 0; i < g_strv_length(par); i++)
1348 sharg_append(a, par[i]);
1350 c[0](uzbl.gui.web_view, a);
1352 g_array_free (a, TRUE);
1355 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1362 if(*uzbl.net.proxy_url == ' '
1363 || uzbl.net.proxy_url == NULL) {
1364 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1365 (GType) SOUP_SESSION_PROXY_URI);
1368 suri = soup_uri_new(uzbl.net.proxy_url);
1369 g_object_set(G_OBJECT(uzbl.net.soup_session),
1370 SOUP_SESSION_PROXY_URI,
1372 soup_uri_free(suri);
1379 if(file_exists(uzbl.gui.icon)) {
1380 if (uzbl.gui.main_window)
1381 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1383 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1385 g_free (uzbl.gui.icon);
1390 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1391 g_array_append_val (a, uzbl.state.uri);
1392 load_uri(uzbl.gui.web_view, a);
1393 g_array_free (a, TRUE);
1397 cmd_always_insert_mode() {
1398 uzbl.behave.insert_mode =
1399 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1405 g_object_set(G_OBJECT(uzbl.net.soup_session),
1406 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1410 cmd_max_conns_host() {
1411 g_object_set(G_OBJECT(uzbl.net.soup_session),
1412 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1417 soup_session_remove_feature
1418 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1419 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1420 /*g_free(uzbl.net.soup_logger);*/
1422 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1423 soup_session_add_feature(uzbl.net.soup_session,
1424 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1427 static WebKitWebSettings*
1429 return webkit_web_view_get_settings(uzbl.gui.web_view);
1434 WebKitWebSettings *ws = view_settings();
1435 if (uzbl.behave.font_size > 0) {
1436 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1439 if (uzbl.behave.monospace_size > 0) {
1440 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1441 uzbl.behave.monospace_size, NULL);
1443 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1444 uzbl.behave.font_size, NULL);
1450 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1454 cmd_disable_plugins() {
1455 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1456 !uzbl.behave.disable_plugins, NULL);
1460 cmd_disable_scripts() {
1461 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1462 !uzbl.behave.disable_scripts, NULL);
1466 cmd_minimum_font_size() {
1467 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1468 uzbl.behave.minimum_font_size, NULL);
1471 cmd_autoload_img() {
1472 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1473 uzbl.behave.autoload_img, NULL);
1478 cmd_autoshrink_img() {
1479 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1480 uzbl.behave.autoshrink_img, NULL);
1485 cmd_enable_spellcheck() {
1486 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1487 uzbl.behave.enable_spellcheck, NULL);
1491 cmd_enable_private() {
1492 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1493 uzbl.behave.enable_private, NULL);
1498 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1499 uzbl.behave.print_bg, NULL);
1504 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1505 uzbl.behave.style_uri, NULL);
1509 cmd_resizable_txt() {
1510 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1511 uzbl.behave.resizable_txt, NULL);
1515 cmd_default_encoding() {
1516 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1517 uzbl.behave.default_encoding, NULL);
1521 cmd_enforce_96dpi() {
1522 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1523 uzbl.behave.enforce_96dpi, NULL);
1527 cmd_caret_browsing() {
1528 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1529 uzbl.behave.caret_browsing, NULL);
1533 cmd_cookie_handler() {
1534 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1535 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1536 if ((g_strcmp0(split[0], "sh") == 0) ||
1537 (g_strcmp0(split[0], "spawn") == 0)) {
1538 g_free (uzbl.behave.cookie_handler);
1539 uzbl.behave.cookie_handler =
1540 g_strdup_printf("sync_%s %s", split[0], split[1]);
1547 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1552 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1557 if(uzbl.behave.inject_html) {
1558 webkit_web_view_load_html_string (uzbl.gui.web_view,
1559 uzbl.behave.inject_html, NULL);
1568 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1569 uzbl.behave.modmask = 0;
1571 if(uzbl.behave.modkey)
1572 g_free(uzbl.behave.modkey);
1573 uzbl.behave.modkey = buf;
1575 for (i = 0; modkeys[i].key != NULL; i++) {
1576 if (g_strrstr(buf, modkeys[i].key))
1577 uzbl.behave.modmask |= modkeys[i].mask;
1583 if (*uzbl.net.useragent == ' ') {
1584 g_free (uzbl.net.useragent);
1585 uzbl.net.useragent = NULL;
1587 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1589 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1590 g_free(uzbl.net.useragent);
1591 uzbl.net.useragent = ua;
1597 gtk_widget_ref(uzbl.gui.scrolled_win);
1598 gtk_widget_ref(uzbl.gui.mainbar);
1599 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1600 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1602 if(uzbl.behave.status_top) {
1603 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1604 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1607 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1608 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1610 gtk_widget_unref(uzbl.gui.scrolled_win);
1611 gtk_widget_unref(uzbl.gui.mainbar);
1612 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1617 set_var_value(gchar *name, gchar *val) {
1618 uzbl_cmdprop *c = NULL;
1622 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1623 /* check for the variable type */
1624 if (c->type == TYPE_STR) {
1625 buf = expand_vars(val);
1628 } else if(c->type == TYPE_INT) {
1629 int *ip = (int *)c->ptr;
1630 buf = expand_vars(val);
1631 *ip = (int)strtoul(buf, &endp, 10);
1633 } else if (c->type == TYPE_FLOAT) {
1634 float *fp = (float *)c->ptr;
1635 buf = expand_vars(val);
1636 *fp = strtod(buf, &endp);
1640 /* invoke a command specific function */
1641 if(c->func) c->func();
1648 Behaviour *b = &uzbl.behave;
1650 if(b->html_buffer->str) {
1651 webkit_web_view_load_html_string (uzbl.gui.web_view,
1652 b->html_buffer->str, b->base_url);
1653 g_string_free(b->html_buffer, TRUE);
1654 b->html_buffer = g_string_new("");
1658 enum {M_CMD, M_HTML};
1660 parse_cmd_line(const char *ctl_line) {
1661 Behaviour *b = &uzbl.behave;
1664 if(b->mode == M_HTML) {
1665 len = strlen(b->html_endmarker);
1666 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1667 if(len == strlen(ctl_line)-1 &&
1668 !strncmp(b->html_endmarker, ctl_line, len)) {
1670 set_var_value("mode", "0");
1675 set_timeout(b->html_timeout);
1676 g_string_append(b->html_buffer, ctl_line);
1679 else if((ctl_line[0] == '#') /* Comments */
1680 || (ctl_line[0] == ' ')
1681 || (ctl_line[0] == '\n'))
1682 ; /* ignore these lines */
1683 else { /* parse a command */
1685 gchar **tokens = NULL;
1686 len = strlen(ctl_line);
1688 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1689 ctlstrip = g_strndup(ctl_line, len - 1);
1690 else ctlstrip = g_strdup(ctl_line);
1692 tokens = g_strsplit(ctlstrip, " ", 2);
1693 parse_command(tokens[0], tokens[1]);
1700 build_stream_name(int type, const gchar* dir) {
1701 char *xwin_str = NULL;
1702 State *s = &uzbl.state;
1705 xwin_str = itos((int)uzbl.xwin);
1707 str = g_strdup_printf
1708 ("%s/uzbl_fifo_%s", dir,
1709 s->instance_name ? s->instance_name : xwin_str);
1710 } else if (type == SOCKET) {
1711 str = g_strdup_printf
1712 ("%s/uzbl_socket_%s", dir,
1713 s->instance_name ? s->instance_name : xwin_str );
1720 control_fifo(GIOChannel *gio, GIOCondition condition) {
1721 if (uzbl.state.verbose)
1722 printf("triggered\n");
1727 if (condition & G_IO_HUP)
1728 g_error ("Fifo: Read end of pipe died!\n");
1731 g_error ("Fifo: GIOChannel broke\n");
1733 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1734 if (ret == G_IO_STATUS_ERROR) {
1735 g_error ("Fifo: Error reading: %s\n", err->message);
1739 parse_cmd_line(ctl_line);
1746 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1747 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1748 if (unlink(uzbl.comm.fifo_path) == -1)
1749 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1750 g_free(uzbl.comm.fifo_path);
1751 uzbl.comm.fifo_path = NULL;
1754 if (*dir == ' ') { /* space unsets the variable */
1759 GIOChannel *chan = NULL;
1760 GError *error = NULL;
1761 gchar *path = build_stream_name(FIFO, dir);
1763 if (!file_exists(path)) {
1764 if (mkfifo (path, 0666) == 0) {
1765 // 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.
1766 chan = g_io_channel_new_file(path, "r+", &error);
1768 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1769 if (uzbl.state.verbose)
1770 printf ("init_fifo: created successfully as %s\n", path);
1771 uzbl.comm.fifo_path = path;
1773 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1774 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1775 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1776 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1778 /* if we got this far, there was an error; cleanup */
1779 if (error) g_error_free (error);
1786 control_stdin(GIOChannel *gio, GIOCondition condition) {
1788 gchar *ctl_line = NULL;
1791 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1792 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1795 parse_cmd_line(ctl_line);
1803 GIOChannel *chan = NULL;
1804 GError *error = NULL;
1806 chan = g_io_channel_unix_new(fileno(stdin));
1808 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1809 g_error ("Stdin: could not add watch\n");
1811 if (uzbl.state.verbose)
1812 printf ("Stdin: watch added successfully\n");
1815 g_error ("Stdin: Error while opening: %s\n", error->message);
1817 if (error) g_error_free (error);
1821 control_socket(GIOChannel *chan) {
1822 struct sockaddr_un remote;
1823 char buffer[512], *ctl_line;
1825 int sock, clientsock, n, done;
1828 sock = g_io_channel_unix_get_fd(chan);
1830 memset (buffer, 0, sizeof (buffer));
1832 t = sizeof (remote);
1833 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1837 memset (temp, 0, sizeof (temp));
1838 n = recv (clientsock, temp, 128, 0);
1840 buffer[strlen (buffer)] = '\0';
1844 strcat (buffer, temp);
1847 if (strcmp (buffer, "\n") < 0) {
1848 buffer[strlen (buffer) - 1] = '\0';
1850 buffer[strlen (buffer)] = '\0';
1853 ctl_line = g_strdup(buffer);
1854 parse_cmd_line (ctl_line);
1857 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1858 GError *error = NULL;
1861 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1862 if (ret == G_IO_STATUS_ERROR)
1863 g_error ("Error reading: %s\n", error->message);
1865 printf("Got line %s (%u bytes) \n",ctl_line, len);
1867 parse_line(ctl_line);
1875 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1876 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1877 if (unlink(uzbl.comm.socket_path) == -1)
1878 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1879 g_free(uzbl.comm.socket_path);
1880 uzbl.comm.socket_path = NULL;
1888 GIOChannel *chan = NULL;
1890 struct sockaddr_un local;
1891 gchar *path = build_stream_name(SOCKET, dir);
1893 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1895 local.sun_family = AF_UNIX;
1896 strcpy (local.sun_path, path);
1897 unlink (local.sun_path);
1899 len = strlen (local.sun_path) + sizeof (local.sun_family);
1900 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1901 if (uzbl.state.verbose)
1902 printf ("init_socket: opened in %s\n", path);
1905 if( (chan = g_io_channel_unix_new(sock)) ) {
1906 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1907 uzbl.comm.socket_path = path;
1910 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1912 /* if we got this far, there was an error; cleanup */
1919 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1920 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1922 // this function may be called very early when the templates are not set (yet), hence the checks
1924 update_title (void) {
1925 Behaviour *b = &uzbl.behave;
1928 if (b->show_status) {
1929 if (b->title_format_short) {
1930 parsed = expand_template(b->title_format_short, FALSE);
1931 if (uzbl.gui.main_window)
1932 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1935 if (b->status_format) {
1936 parsed = expand_template(b->status_format, TRUE);
1937 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1940 if (b->status_background) {
1942 gdk_color_parse (b->status_background, &color);
1943 //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)
1944 if (uzbl.gui.main_window)
1945 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1948 if (b->title_format_long) {
1949 parsed = expand_template(b->title_format_long, FALSE);
1950 if (uzbl.gui.main_window)
1951 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1958 key_press_cb (GtkWidget* window, GdkEventKey* event)
1960 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1964 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1965 || 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)
1968 /* turn off insert mode (if always_insert_mode is not used) */
1969 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1970 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1975 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1978 if (event->keyval == GDK_Escape) {
1979 g_string_truncate(uzbl.state.keycmd, 0);
1981 dehilight(uzbl.gui.web_view, NULL);
1985 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1986 if (event->keyval == GDK_Insert) {
1988 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1989 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1991 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1994 g_string_append (uzbl.state.keycmd, str);
2001 if (event->keyval == GDK_BackSpace)
2002 keycmd_bs(NULL, NULL);
2004 gboolean key_ret = FALSE;
2005 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2007 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2009 run_keycmd(key_ret);
2011 if (key_ret) return (!uzbl.behave.insert_mode);
2016 run_keycmd(const gboolean key_ret) {
2017 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2019 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2020 g_string_truncate(uzbl.state.keycmd, 0);
2021 parse_command(act->name, act->param);
2025 /* try if it's an incremental keycmd or one that takes args, and run it */
2026 GString* short_keys = g_string_new ("");
2027 GString* short_keys_inc = g_string_new ("");
2029 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2030 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2031 g_string_assign(short_keys_inc, short_keys->str);
2032 g_string_append_c(short_keys, '_');
2033 g_string_append_c(short_keys_inc, '*');
2035 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2036 /* run normal cmds only if return was pressed */
2037 exec_paramcmd(act, i);
2038 g_string_truncate(uzbl.state.keycmd, 0);
2040 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2041 if (key_ret) /* just quit the incremental command on return */
2042 g_string_truncate(uzbl.state.keycmd, 0);
2043 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2047 g_string_truncate(short_keys, short_keys->len - 1);
2049 g_string_free (short_keys, TRUE);
2050 g_string_free (short_keys_inc, TRUE);
2054 exec_paramcmd(const Action *act, const guint i) {
2055 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2056 GString *actionname = g_string_new ("");
2057 GString *actionparam = g_string_new ("");
2058 g_string_erase (parampart, 0, i+1);
2060 g_string_printf (actionname, act->name, parampart->str);
2062 g_string_printf (actionparam, act->param, parampart->str);
2063 parse_command(actionname->str, actionparam->str);
2064 g_string_free(actionname, TRUE);
2065 g_string_free(actionparam, TRUE);
2066 g_string_free(parampart, TRUE);
2074 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2075 //main_window_ref = g_object_ref(scrolled_window);
2076 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
2078 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2079 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2081 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2082 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2083 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2084 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2085 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2086 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2087 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2088 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2089 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2090 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2091 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2093 return scrolled_window;
2100 g->mainbar = gtk_hbox_new (FALSE, 0);
2102 /* keep a reference to the bar so we can re-pack it at runtime*/
2103 //sbar_ref = g_object_ref(g->mainbar);
2105 g->mainbar_label = gtk_label_new ("");
2106 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2107 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2108 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2109 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2110 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2111 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2116 GtkWidget* create_window () {
2117 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2118 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2119 gtk_widget_set_name (window, "Uzbl browser");
2120 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2121 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2127 GtkPlug* create_plug () {
2128 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2129 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2130 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2137 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2139 If actname is one that calls an external command, this function will inject
2140 newargs in front of the user-provided args in that command line. They will
2141 come become after the body of the script (in sh) or after the name of
2142 the command to execute (in spawn).
2143 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2144 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2146 The return value consist of two strings: the action (sh, ...) and its args.
2148 If act is not one that calls an external command, then the given action merely
2151 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2152 gchar *actdup = g_strdup(actname);
2153 g_array_append_val(rets, actdup);
2155 if ((g_strcmp0(actname, "spawn") == 0) ||
2156 (g_strcmp0(actname, "sh") == 0) ||
2157 (g_strcmp0(actname, "sync_spawn") == 0) ||
2158 (g_strcmp0(actname, "sync_sh") == 0)) {
2160 GString *a = g_string_new("");
2161 gchar **spawnparts = split_quoted(origargs, FALSE);
2162 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2163 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2165 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2166 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2168 g_array_append_val(rets, a->str);
2169 g_string_free(a, FALSE);
2170 g_strfreev(spawnparts);
2172 gchar *origdup = g_strdup(origargs);
2173 g_array_append_val(rets, origdup);
2175 return (gchar**)g_array_free(rets, FALSE);
2179 run_handler (const gchar *act, const gchar *args) {
2180 /* Consider this code a temporary hack to make the handlers usable.
2181 In practice, all this splicing, injection, and reconstruction is
2182 inefficient, annoying and hard to manage. Potential pitfalls arise
2183 when the handler specific args 1) are not quoted (the handler
2184 callbacks should take care of this) 2) are quoted but interfere
2185 with the users' own quotation. A more ideal solution is
2186 to refactor parse_command so that it doesn't just take a string
2187 and execute it; rather than that, we should have a function which
2188 returns the argument vector parsed from the string. This vector
2189 could be modified (e.g. insert additional args into it) before
2190 passing it to the next function that actually executes it. Though
2191 it still isn't perfect for chain actions.. will reconsider & re-
2192 factor when I have the time. -duc */
2194 char **parts = g_strsplit(act, " ", 2);
2196 if (g_strcmp0(parts[0], "chain") == 0) {
2197 GString *newargs = g_string_new("");
2198 gchar **chainparts = split_quoted(parts[1], FALSE);
2200 /* for every argument in the chain, inject the handler args
2201 and make sure the new parts are wrapped in quotes */
2202 gchar **cp = chainparts;
2204 gchar *quotless = NULL;
2205 gchar **spliced_quotless = NULL; // sigh -_-;
2206 gchar **inpart = NULL;
2209 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2211 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2212 } else quotless = g_strdup(*cp);
2214 spliced_quotless = g_strsplit(quotless, " ", 2);
2215 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2216 g_strfreev(spliced_quotless);
2218 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2224 parse_command(parts[0], &(newargs->str[1]));
2225 g_string_free(newargs, TRUE);
2226 g_strfreev(chainparts);
2229 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2230 parse_command(inparts[0], inparts[1]);
2238 add_binding (const gchar *key, const gchar *act) {
2239 char **parts = g_strsplit(act, " ", 2);
2246 if (uzbl.state.verbose)
2247 printf ("Binding %-10s : %s\n", key, act);
2248 action = new_action(parts[0], parts[1]);
2250 if (g_hash_table_remove (uzbl.bindings, key))
2251 g_warning ("Overwriting existing binding for \"%s\"", key);
2252 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2257 get_xdg_var (XDG_Var xdg) {
2258 const gchar* actual_value = getenv (xdg.environmental);
2259 const gchar* home = getenv ("HOME");
2260 gchar* return_value;
2262 if (! actual_value || strcmp (actual_value, "") == 0) {
2263 if (xdg.default_value) {
2264 return_value = str_replace ("~", home, xdg.default_value);
2266 return_value = NULL;
2269 return_value = str_replace("~", home, actual_value);
2272 return return_value;
2276 find_xdg_file (int xdg_type, char* filename) {
2277 /* xdg_type = 0 => config
2278 xdg_type = 1 => data
2279 xdg_type = 2 => cache*/
2281 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2282 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2285 gchar* temporary_string;
2289 if (! file_exists (temporary_file) && xdg_type != 2) {
2290 buf = get_xdg_var (XDG[3 + xdg_type]);
2291 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2294 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2295 g_free (temporary_file);
2296 temporary_file = g_strconcat (temporary_string, filename, NULL);
2300 //g_free (temporary_string); - segfaults.
2302 if (file_exists (temporary_file)) {
2303 return temporary_file;
2310 State *s = &uzbl.state;
2311 Network *n = &uzbl.net;
2313 for (i = 0; default_config[i].command != NULL; i++) {
2314 parse_cmd_line(default_config[i].command);
2317 if (!s->config_file) {
2318 s->config_file = find_xdg_file (0, "/uzbl/config");
2321 if (s->config_file) {
2322 GArray* lines = read_file_by_line (s->config_file);
2326 while ((line = g_array_index(lines, gchar*, i))) {
2327 parse_cmd_line (line);
2331 g_array_free (lines, TRUE);
2333 if (uzbl.state.verbose)
2334 printf ("No configuration file loaded.\n");
2337 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2340 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2343 if (!uzbl.behave.cookie_handler)
2346 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2347 GString *s = g_string_new ("");
2348 SoupURI * soup_uri = soup_message_get_uri(msg);
2349 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2350 run_handler(uzbl.behave.cookie_handler, s->str);
2352 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2353 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2354 if ( p != NULL ) *p = '\0';
2355 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2357 if (uzbl.comm.sync_stdout)
2358 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2360 g_string_free(s, TRUE);
2364 save_cookies (SoupMessage *msg, gpointer user_data){
2368 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2369 cookie = soup_cookie_to_set_cookie_header(ck->data);
2370 SoupURI * soup_uri = soup_message_get_uri(msg);
2371 GString *s = g_string_new ("");
2372 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2373 run_handler(uzbl.behave.cookie_handler, s->str);
2375 g_string_free(s, TRUE);
2380 /* --- WEBINSPECTOR --- */
2382 hide_window_cb(GtkWidget *widget, gpointer data) {
2385 gtk_widget_hide(widget);
2388 static WebKitWebView*
2389 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2392 (void) web_inspector;
2393 GtkWidget* scrolled_window;
2394 GtkWidget* new_web_view;
2397 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2398 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2399 G_CALLBACK(hide_window_cb), NULL);
2401 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2402 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2403 gtk_widget_show(g->inspector_window);
2405 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2406 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2407 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2408 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2409 gtk_widget_show(scrolled_window);
2411 new_web_view = webkit_web_view_new();
2412 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2414 return WEBKIT_WEB_VIEW(new_web_view);
2418 inspector_show_window_cb (WebKitWebInspector* inspector){
2420 gtk_widget_show(uzbl.gui.inspector_window);
2424 /* TODO: Add variables and code to make use of these functions */
2426 inspector_close_window_cb (WebKitWebInspector* inspector){
2432 inspector_attach_window_cb (WebKitWebInspector* inspector){
2438 inspector_detach_window_cb (WebKitWebInspector* inspector){
2444 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2450 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2456 set_up_inspector() {
2458 WebKitWebSettings *settings = view_settings();
2459 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2461 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2462 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2463 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2464 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2465 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2466 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2467 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2469 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2473 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2475 uzbl_cmdprop *c = v;
2480 if(c->type == TYPE_STR)
2481 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2482 else if(c->type == TYPE_INT)
2483 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2487 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2491 printf("bind %s = %s %s\n", (char *)k ,
2492 (char *)a->name, a->param?(char *)a->param:"");
2497 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2498 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2503 main (int argc, char* argv[]) {
2504 gtk_init (&argc, &argv);
2505 if (!g_thread_supported ())
2506 g_thread_init (NULL);
2507 uzbl.state.executable_path = g_strdup(argv[0]);
2508 uzbl.state.selected_url = NULL;
2509 uzbl.state.searchtx = NULL;
2511 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2512 g_option_context_add_main_entries (context, entries, NULL);
2513 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2514 g_option_context_parse (context, &argc, &argv, NULL);
2515 g_option_context_free(context);
2517 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2518 gboolean verbose_override = uzbl.state.verbose;
2520 /* initialize hash table */
2521 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2523 uzbl.net.soup_session = webkit_get_default_session();
2524 uzbl.state.keycmd = g_string_new("");
2526 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2527 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2528 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2529 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2530 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2531 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2534 if(uname(&uzbl.state.unameinfo) == -1)
2535 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2537 uzbl.gui.sbar.progress_s = g_strdup("=");
2538 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2539 uzbl.gui.sbar.progress_w = 10;
2541 /* HTML mode defaults*/
2542 uzbl.behave.html_buffer = g_string_new("");
2543 uzbl.behave.html_endmarker = g_strdup(".");
2544 uzbl.behave.html_timeout = 60;
2545 uzbl.behave.base_url = g_strdup("http://invalid");
2547 /* default mode indicators */
2548 uzbl.behave.insert_indicator = g_strdup("I");
2549 uzbl.behave.cmd_indicator = g_strdup("C");
2553 make_var_to_name_hash();
2555 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2557 uzbl.gui.scrolled_win = create_browser();
2560 /* initial packing */
2561 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2562 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2564 if (uzbl.state.socket_id) {
2565 uzbl.gui.plug = create_plug ();
2566 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2567 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2569 uzbl.gui.main_window = create_window ();
2570 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2571 gtk_widget_show_all (uzbl.gui.main_window);
2572 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2575 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2577 if (uzbl.state.verbose) {
2578 printf("Uzbl start location: %s\n", argv[0]);
2579 if (uzbl.state.socket_id)
2580 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2582 printf("window_id %i\n",(int) uzbl.xwin);
2583 printf("pid %i\n", getpid ());
2584 printf("name: %s\n", uzbl.state.instance_name);
2587 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2588 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2589 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2590 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2591 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2595 if (!uzbl.behave.show_status)
2596 gtk_widget_hide(uzbl.gui.mainbar);
2605 if (verbose_override > uzbl.state.verbose)
2606 uzbl.state.verbose = verbose_override;
2609 set_var_value("uri", uri_override);
2610 g_free(uri_override);
2611 } else if (uzbl.state.uri)
2612 cmd_load_uri(uzbl.gui.web_view, NULL);
2617 return EXIT_SUCCESS;
2620 /* vi: set et ts=4: */