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>
63 /* commandline arguments (set initial values for the state variables) */
65 GOptionEntry entries[] =
67 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
68 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
69 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
70 "Whether to print all messages or just errors.", NULL },
71 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
72 "Name of the current instance (defaults to Xorg window id)", "NAME" },
73 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
74 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
75 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
76 "Socket ID", "SOCKET" },
77 { NULL, 0, 0, 0, NULL, NULL, NULL }
80 /* associate command names to their properties */
81 typedef const struct {
88 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
90 /* an abbreviation to help keep the table's width humane */
91 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
96 } var_name_to_ptr[] = {
97 /* variable name pointer to variable in code type dump callback function */
98 /* --------------------------------------------------------------------------------------- */
99 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
100 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
101 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
102 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
103 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
104 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
105 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
106 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
107 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
108 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
109 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
110 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
111 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
112 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
113 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
114 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
115 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
116 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
117 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
118 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
119 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
120 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
121 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
122 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
123 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
124 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
125 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
126 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
127 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
128 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
129 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
130 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
131 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
132 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
133 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
134 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
135 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
136 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
137 /* exported WebKitWebSettings properties */
138 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
139 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
140 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
141 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
142 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
143 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
144 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
145 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
146 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
147 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
148 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
149 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
150 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
151 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
152 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
153 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
155 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
156 }, *n2v_p = var_name_to_ptr;
162 { "SHIFT", GDK_SHIFT_MASK }, // shift
163 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
164 { "CONTROL", GDK_CONTROL_MASK }, // control
165 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
166 { "MOD2", GDK_MOD2_MASK }, // 5th mod
167 { "MOD3", GDK_MOD3_MASK }, // 6th mod
168 { "MOD4", GDK_MOD4_MASK }, // 7th mod
169 { "MOD5", GDK_MOD5_MASK }, // 8th mod
170 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
171 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
172 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
173 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
174 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
175 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
176 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
177 { "META", GDK_META_MASK }, // meta (since 2.10)
182 /* construct a hash from the var_name_to_ptr array for quick access */
184 make_var_to_name_hash() {
185 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
187 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
192 /* --- UTILITY FUNCTIONS --- */
194 expand_vars(char *s) {
197 char ret[256], *vend;
198 GString *buf = g_string_new("");
203 g_string_append_c(buf, *++s);
211 if( (vend = strchr(s, upto)) ||
212 (vend = strchr(s, '\0')) ) {
213 strncpy(ret, s, vend-s);
215 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
216 if(c->type == TYPE_STR)
217 g_string_append(buf, (gchar *)*c->ptr);
218 else if(c->type == TYPE_INT) {
219 char *b = itos((int)*c->ptr);
220 g_string_append(buf, b);
224 if(upto == ' ') s = vend;
230 g_string_append_c(buf, *s);
235 return g_string_free(buf, FALSE);
242 snprintf(tmp, sizeof(tmp), "%i", val);
243 return g_strdup(tmp);
247 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
250 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
253 str_replace (const char* search, const char* replace, const char* string) {
257 buf = g_strsplit (string, search, -1);
258 ret = g_strjoinv (replace, buf);
259 g_strfreev(buf); // somebody said this segfaults
265 read_file_by_line (gchar *path) {
266 GIOChannel *chan = NULL;
267 gchar *readbuf = NULL;
269 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
272 chan = g_io_channel_new_file(path, "r", NULL);
275 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
276 const gchar* val = g_strdup (readbuf);
277 g_array_append_val (lines, val);
282 g_io_channel_unref (chan);
284 fprintf(stderr, "File '%s' not be read.\n", path);
291 gchar* parseenv (char* string) {
292 extern char** environ;
293 gchar* tmpstr = NULL;
297 while (environ[i] != NULL) {
298 gchar** env = g_strsplit (environ[i], "=", 2);
299 gchar* envname = g_strconcat ("$", env[0], NULL);
301 if (g_strrstr (string, envname) != NULL) {
302 tmpstr = g_strdup(string);
304 string = str_replace(envname, env[1], tmpstr);
309 g_strfreev (env); // somebody said this breaks uzbl
317 setup_signal(int signr, sigfunc *shandler) {
318 struct sigaction nh, oh;
320 nh.sa_handler = shandler;
321 sigemptyset(&nh.sa_mask);
324 if(sigaction(signr, &nh, &oh) < 0)
332 if (uzbl.behave.fifo_dir)
333 unlink (uzbl.comm.fifo_path);
334 if (uzbl.behave.socket_dir)
335 unlink (uzbl.comm.socket_path);
337 g_free(uzbl.state.executable_path);
338 g_string_free(uzbl.state.keycmd, TRUE);
339 g_hash_table_destroy(uzbl.bindings);
340 g_hash_table_destroy(uzbl.behave.commands);
343 /* used for html_mode_timeout
344 * be sure to extend this function to use
345 * more timers if needed in other places
348 set_timeout(int seconds) {
350 memset(&t, 0, sizeof t);
352 t.it_value.tv_sec = seconds;
353 t.it_value.tv_usec = 0;
354 setitimer(ITIMER_REAL, &t, NULL);
357 /* --- SIGNAL HANDLER --- */
360 catch_sigterm(int s) {
366 catch_sigint(int s) {
376 set_var_value("mode", "0");
381 /* --- CALLBACKS --- */
384 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
387 (void) navigation_action;
388 (void) policy_decision;
390 const gchar* uri = webkit_network_request_get_uri (request);
391 if (uzbl.state.verbose)
392 printf("New window requested -> %s \n", uri);
393 new_window_load_uri(uri);
398 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
403 /* If we can display it, let's display it... */
404 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
405 webkit_web_policy_decision_use (policy_decision);
409 /* ...everything we can't displayed is downloaded */
410 webkit_web_policy_decision_download (policy_decision);
415 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
419 if (uzbl.state.selected_url != NULL) {
420 if (uzbl.state.verbose)
421 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
422 new_window_load_uri(uzbl.state.selected_url);
424 if (uzbl.state.verbose)
425 printf("New web view -> %s\n","Nothing to open, exiting");
431 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
434 if (uzbl.behave.download_handler) {
435 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
436 if (uzbl.state.verbose)
437 printf("Download -> %s\n",uri);
438 /* if urls not escaped, we may have to escape and quote uri before this call */
439 run_handler(uzbl.behave.download_handler, uri);
444 /* scroll a bar in a given direction */
446 scroll (GtkAdjustment* bar, GArray *argv) {
450 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
451 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
452 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
456 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
457 (void) page; (void) argv; (void) result;
458 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
462 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
463 (void) page; (void) argv; (void) result;
464 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
465 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
469 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
470 (void) page; (void) result;
471 scroll(uzbl.gui.bar_v, argv);
475 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
476 (void) page; (void) result;
477 scroll(uzbl.gui.bar_h, argv);
482 if (!uzbl.behave.show_status) {
483 gtk_widget_hide(uzbl.gui.mainbar);
485 gtk_widget_show(uzbl.gui.mainbar);
491 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
496 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
500 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
505 if (uzbl.behave.show_status) {
506 gtk_widget_hide(uzbl.gui.mainbar);
508 gtk_widget_show(uzbl.gui.mainbar);
510 uzbl.behave.show_status = !uzbl.behave.show_status;
515 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
519 //Set selected_url state variable
520 g_free(uzbl.state.selected_url);
521 uzbl.state.selected_url = NULL;
523 uzbl.state.selected_url = g_strdup(link);
529 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
533 if (uzbl.gui.main_title)
534 g_free (uzbl.gui.main_title);
535 uzbl.gui.main_title = g_strdup (title);
540 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
543 uzbl.gui.sbar.load_progress = progress;
548 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
552 if (uzbl.behave.load_finish_handler)
553 run_handler(uzbl.behave.load_finish_handler, "");
557 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
561 uzbl.gui.sbar.load_progress = 0;
562 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
563 if (uzbl.behave.load_start_handler)
564 run_handler(uzbl.behave.load_start_handler, "");
568 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
571 g_free (uzbl.state.uri);
572 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
573 uzbl.state.uri = g_string_free (newuri, FALSE);
574 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
575 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
578 if (uzbl.behave.load_commit_handler)
579 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
583 destroy_cb (GtkWidget* widget, gpointer data) {
591 if (uzbl.behave.history_handler) {
593 struct tm * timeinfo;
596 timeinfo = localtime ( &rawtime );
597 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
598 run_handler(uzbl.behave.history_handler, date);
603 /* VIEW funcs (little webkit wrappers) */
604 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
606 VIEWFUNC(reload_bypass_cache)
607 VIEWFUNC(stop_loading)
614 /* -- command to callback/function map for things we cannot attach to any signals */
615 static struct {char *key; CommandInfo value;} cmdlist[] =
616 { /* key function no_split */
617 { "back", {view_go_back, 0} },
618 { "forward", {view_go_forward, 0} },
619 { "scroll_vert", {scroll_vert, 0} },
620 { "scroll_horz", {scroll_horz, 0} },
621 { "scroll_begin", {scroll_begin, 0} },
622 { "scroll_end", {scroll_end, 0} },
623 { "reload", {view_reload, 0}, },
624 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
625 { "stop", {view_stop_loading, 0}, },
626 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
627 { "zoom_out", {view_zoom_out, 0}, },
628 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
629 { "uri", {load_uri, TRUE} },
630 { "js", {run_js, TRUE} },
631 { "script", {run_external_js, 0} },
632 { "toggle_status", {toggle_status_cb, 0} },
633 { "spawn", {spawn, 0} },
634 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
635 { "sh", {spawn_sh, 0} },
636 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
637 { "exit", {close_uzbl, 0} },
638 { "search", {search_forward_text, TRUE} },
639 { "search_reverse", {search_reverse_text, TRUE} },
640 { "dehilight", {dehilight, 0} },
641 { "toggle_insert_mode", {toggle_insert_mode, 0} },
642 { "set", {set_var, TRUE} },
643 //{ "get", {get_var, TRUE} },
644 { "bind", {act_bind, TRUE} },
645 { "dump_config", {act_dump_config, 0} },
646 { "keycmd", {keycmd, TRUE} },
647 { "keycmd_nl", {keycmd_nl, TRUE} },
648 { "keycmd_bs", {keycmd_bs, 0} },
649 { "chain", {chain, 0} },
650 { "print", {print, TRUE} }
657 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
659 for (i = 0; i < LENGTH(cmdlist); i++)
660 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
663 /* -- CORE FUNCTIONS -- */
666 free_action(gpointer act) {
667 Action *action = (Action*)act;
668 g_free(action->name);
670 g_free(action->param);
675 new_action(const gchar *name, const gchar *param) {
676 Action *action = g_new(Action, 1);
678 action->name = g_strdup(name);
680 action->param = g_strdup(param);
682 action->param = NULL;
688 file_exists (const char * filename) {
689 return (access(filename, F_OK) == 0);
693 set_var(WebKitWebView *page, GArray *argv, GString *result) {
694 (void) page; (void) result;
695 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
696 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
697 set_var_value(g_strstrip(split[0]), value);
703 print(WebKitWebView *page, GArray *argv, GString *result) {
704 (void) page; (void) result;
707 buf = expand_vars(argv_idx(argv, 0));
708 g_string_assign(result, buf);
713 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
714 (void) page; (void) result;
715 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
716 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
717 add_binding(g_strstrip(split[0]), value);
729 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
730 (void) page; (void) result;
732 if (argv_idx(argv, 0)) {
733 if (strcmp (argv_idx(argv, 0), "0") == 0) {
734 uzbl.behave.insert_mode = FALSE;
736 uzbl.behave.insert_mode = TRUE;
739 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
746 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
749 if (argv_idx(argv, 0)) {
750 GString* newuri = g_string_new (argv_idx(argv, 0));
751 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
752 run_js(web_view, argv, NULL);
755 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
756 g_string_prepend (newuri, "http://");
757 /* if we do handle cookies, ask our handler for them */
758 webkit_web_view_load_uri (web_view, newuri->str);
759 g_string_free (newuri, TRUE);
764 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
767 if (argv_idx(argv, 0))
768 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
772 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
774 if (argv_idx(argv, 0)) {
775 GArray* lines = read_file_by_line (argv_idx (argv, 0));
780 while ((line = g_array_index(lines, gchar*, i))) {
782 js = g_strdup (line);
784 gchar* newjs = g_strconcat (js, line, NULL);
791 if (uzbl.state.verbose)
792 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
794 if (argv_idx (argv, 1)) {
795 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
799 webkit_web_view_execute_script (web_view, js);
801 g_array_free (lines, TRUE);
806 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
807 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
808 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
809 webkit_web_view_unmark_text_matches (page);
810 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
811 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
815 if (uzbl.state.searchtx) {
816 if (uzbl.state.verbose)
817 printf ("Searching: %s\n", uzbl.state.searchtx);
818 webkit_web_view_set_highlight_text_matches (page, TRUE);
819 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
824 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
826 search_text(page, argv, TRUE);
830 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
832 search_text(page, argv, FALSE);
836 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
837 (void) argv; (void) result;
838 webkit_web_view_set_highlight_text_matches (page, FALSE);
843 new_window_load_uri (const gchar * uri) {
844 GString* to_execute = g_string_new ("");
845 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
847 for (i = 0; entries[i].long_name != NULL; i++) {
848 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
849 gchar** str = (gchar**)entries[i].arg_data;
851 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
855 if (uzbl.state.verbose)
856 printf("\n%s\n", to_execute->str);
857 g_spawn_command_line_async (to_execute->str, NULL);
858 g_string_free (to_execute, TRUE);
862 chain (WebKitWebView *page, GArray *argv, GString *result) {
863 (void) page; (void) result;
865 gchar **parts = NULL;
867 while ((a = argv_idx(argv, i++))) {
868 parts = g_strsplit (a, " ", 2);
869 parse_command(parts[0], parts[1], result);
875 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
879 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
885 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
889 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
895 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
899 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
904 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
911 /* --Statusbar functions-- */
913 build_progressbar_ascii(int percent) {
914 int width=uzbl.gui.sbar.progress_w;
917 GString *bar = g_string_new("");
919 l = (double)percent*((double)width/100.);
920 l = (int)(l+.5)>=(int)l ? l+.5 : l;
922 for(i=0; i<(int)l; i++)
923 g_string_append(bar, uzbl.gui.sbar.progress_s);
926 g_string_append(bar, uzbl.gui.sbar.progress_u);
928 return g_string_free(bar, FALSE);
933 const GScannerConfig scan_config = {
936 ) /* cset_skip_characters */,
941 ) /* cset_identifier_first */,
948 ) /* cset_identifier_nth */,
949 ( "" ) /* cpair_comment_single */,
951 TRUE /* case_sensitive */,
953 FALSE /* skip_comment_multi */,
954 FALSE /* skip_comment_single */,
955 FALSE /* scan_comment_multi */,
956 TRUE /* scan_identifier */,
957 TRUE /* scan_identifier_1char */,
958 FALSE /* scan_identifier_NULL */,
959 TRUE /* scan_symbols */,
960 FALSE /* scan_binary */,
961 FALSE /* scan_octal */,
962 FALSE /* scan_float */,
963 FALSE /* scan_hex */,
964 FALSE /* scan_hex_dollar */,
965 FALSE /* scan_string_sq */,
966 FALSE /* scan_string_dq */,
967 TRUE /* numbers_2_int */,
968 FALSE /* int_2_float */,
969 FALSE /* identifier_2_string */,
970 FALSE /* char_2_token */,
971 FALSE /* symbol_2_token */,
972 TRUE /* scope_0_fallback */,
977 uzbl.scan = g_scanner_new(&scan_config);
978 while(symp->symbol_name) {
979 g_scanner_scope_add_symbol(uzbl.scan, 0,
981 GINT_TO_POINTER(symp->symbol_token));
987 expand_template(const char *template, gboolean escape_markup) {
988 if(!template) return NULL;
990 GTokenType token = G_TOKEN_NONE;
991 GString *ret = g_string_new("");
995 g_scanner_input_text(uzbl.scan, template, strlen(template));
996 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
997 token = g_scanner_get_next_token(uzbl.scan);
999 if(token == G_TOKEN_SYMBOL) {
1000 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1004 buf = uzbl.state.uri?
1005 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1006 g_string_append(ret, buf);
1010 g_string_append(ret, uzbl.state.uri?
1011 uzbl.state.uri:g_strdup(""));
1014 buf = itos(uzbl.gui.sbar.load_progress);
1015 g_string_append(ret, buf);
1018 case SYM_LOADPRGSBAR:
1019 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1020 g_string_append(ret, buf);
1025 buf = uzbl.gui.main_title?
1026 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1027 g_string_append(ret, buf);
1031 g_string_append(ret, uzbl.gui.main_title?
1032 uzbl.gui.main_title:g_strdup(""));
1034 case SYM_SELECTED_URI:
1036 buf = uzbl.state.selected_url?
1037 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1038 g_string_append(ret, buf);
1042 g_string_append(ret, uzbl.state.selected_url?
1043 uzbl.state.selected_url:g_strdup(""));
1046 buf = itos(uzbl.xwin);
1047 g_string_append(ret,
1048 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1053 buf = uzbl.state.keycmd->str?
1054 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1055 g_string_append(ret, buf);
1059 g_string_append(ret, uzbl.state.keycmd->str?
1060 uzbl.state.keycmd->str:g_strdup(""));
1063 g_string_append(ret,
1064 uzbl.behave.insert_mode?
1065 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1068 g_string_append(ret,
1069 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1071 /* useragent syms */
1073 buf = itos(WEBKIT_MAJOR_VERSION);
1074 g_string_append(ret, buf);
1078 buf = itos(WEBKIT_MINOR_VERSION);
1079 g_string_append(ret, buf);
1083 buf = itos(WEBKIT_MICRO_VERSION);
1084 g_string_append(ret, buf);
1088 g_string_append(ret, uzbl.state.unameinfo.sysname);
1091 g_string_append(ret, uzbl.state.unameinfo.nodename);
1094 g_string_append(ret, uzbl.state.unameinfo.release);
1097 g_string_append(ret, uzbl.state.unameinfo.version);
1100 g_string_append(ret, uzbl.state.unameinfo.machine);
1103 g_string_append(ret, ARCH);
1106 case SYM_DOMAINNAME:
1107 g_string_append(ret, uzbl.state.unameinfo.domainname);
1111 g_string_append(ret, COMMIT);
1117 else if(token == G_TOKEN_INT) {
1118 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1119 g_string_append(ret, buf);
1122 else if(token == G_TOKEN_IDENTIFIER) {
1123 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1125 else if(token == G_TOKEN_CHAR) {
1126 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1130 return g_string_free(ret, FALSE);
1132 /* --End Statusbar functions-- */
1135 sharg_append(GArray *a, const gchar *str) {
1136 const gchar *s = (str ? str : "");
1137 g_array_append_val(a, s);
1140 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1142 run_command (const gchar *command, const guint npre, const gchar **args,
1143 const gboolean sync, char **output_stdout) {
1144 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1147 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1148 gchar *pid = itos(getpid());
1149 gchar *xwin = itos(uzbl.xwin);
1151 sharg_append(a, command);
1152 for (i = 0; i < npre; i++) /* add n args before the default vars */
1153 sharg_append(a, args[i]);
1154 sharg_append(a, uzbl.state.config_file);
1155 sharg_append(a, pid);
1156 sharg_append(a, xwin);
1157 sharg_append(a, uzbl.comm.fifo_path);
1158 sharg_append(a, uzbl.comm.socket_path);
1159 sharg_append(a, uzbl.state.uri);
1160 sharg_append(a, uzbl.gui.main_title);
1162 for (i = npre; i < g_strv_length((gchar**)args); i++)
1163 sharg_append(a, args[i]);
1167 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1169 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1170 NULL, NULL, output_stdout, NULL, NULL, &err);
1171 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1172 NULL, NULL, NULL, &err);
1174 if (uzbl.state.verbose) {
1175 GString *s = g_string_new("spawned:");
1176 for (i = 0; i < (a->len); i++) {
1177 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1178 g_string_append_printf(s, " %s", qarg);
1181 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1182 printf("%s\n", s->str);
1183 g_string_free(s, TRUE);
1185 printf("Stdout: %s\n", *output_stdout);
1189 g_printerr("error on run_command: %s\n", err->message);
1194 g_array_free (a, TRUE);
1199 split_quoted(const gchar* src, const gboolean unquote) {
1200 /* split on unquoted space, return array of strings;
1201 remove a layer of quotes and backslashes if unquote */
1202 if (!src) return NULL;
1204 gboolean dq = FALSE;
1205 gboolean sq = FALSE;
1206 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1207 GString *s = g_string_new ("");
1211 for (p = src; *p != '\0'; p++) {
1212 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1213 else if (*p == '\\') { g_string_append_c(s, *p++);
1214 g_string_append_c(s, *p); }
1215 else if ((*p == '"') && unquote && !sq) dq = !dq;
1216 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1218 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1219 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1221 else if ((*p == ' ') && !dq && !sq) {
1222 dup = g_strdup(s->str);
1223 g_array_append_val(a, dup);
1224 g_string_truncate(s, 0);
1225 } else g_string_append_c(s, *p);
1227 dup = g_strdup(s->str);
1228 g_array_append_val(a, dup);
1229 ret = (gchar**)a->data;
1230 g_array_free (a, FALSE);
1231 g_string_free (s, TRUE);
1236 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1237 (void)web_view; (void)result;
1238 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1239 if (argv_idx(argv, 0))
1240 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1244 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1245 (void)web_view; (void)result;
1247 if (argv_idx(argv, 0))
1248 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1249 TRUE, &uzbl.comm.sync_stdout);
1253 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1254 (void)web_view; (void)result;
1255 if (!uzbl.behave.shell_cmd) {
1256 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1261 gchar *spacer = g_strdup("");
1262 g_array_insert_val(argv, 1, spacer);
1263 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1265 for (i = 1; i < g_strv_length(cmd); i++)
1266 g_array_prepend_val(argv, cmd[i]);
1268 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1274 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1275 (void)web_view; (void)result;
1276 if (!uzbl.behave.shell_cmd) {
1277 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1282 gchar *spacer = g_strdup("");
1283 g_array_insert_val(argv, 1, spacer);
1284 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1286 for (i = 1; i < g_strv_length(cmd); i++)
1287 g_array_prepend_val(argv, cmd[i]);
1289 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1290 TRUE, &uzbl.comm.sync_stdout);
1296 parse_command(const char *cmd, const char *param, GString *result) {
1299 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1301 gchar **par = split_quoted(param, TRUE);
1302 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1304 if (c->no_split) { /* don't split */
1305 sharg_append(a, param);
1307 for (i = 0; i < g_strv_length(par); i++)
1308 sharg_append(a, par[i]);
1311 if (result == NULL) {
1312 GString *result_print = g_string_new("");
1314 c->function(uzbl.gui.web_view, a, result_print);
1315 if (result_print->len)
1316 printf("%*s\n", result_print->len, result_print->str);
1318 g_string_free(result_print, TRUE);
1320 c->function(uzbl.gui.web_view, a, result);
1323 g_array_free (a, TRUE);
1326 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1333 if(*uzbl.net.proxy_url == ' '
1334 || uzbl.net.proxy_url == NULL) {
1335 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1336 (GType) SOUP_SESSION_PROXY_URI);
1339 suri = soup_uri_new(uzbl.net.proxy_url);
1340 g_object_set(G_OBJECT(uzbl.net.soup_session),
1341 SOUP_SESSION_PROXY_URI,
1343 soup_uri_free(suri);
1350 if(file_exists(uzbl.gui.icon)) {
1351 if (uzbl.gui.main_window)
1352 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1354 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1356 g_free (uzbl.gui.icon);
1361 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1362 g_array_append_val (a, uzbl.state.uri);
1363 load_uri(uzbl.gui.web_view, a, NULL);
1364 g_array_free (a, TRUE);
1368 cmd_always_insert_mode() {
1369 uzbl.behave.insert_mode =
1370 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1376 g_object_set(G_OBJECT(uzbl.net.soup_session),
1377 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1381 cmd_max_conns_host() {
1382 g_object_set(G_OBJECT(uzbl.net.soup_session),
1383 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1388 soup_session_remove_feature
1389 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1390 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1391 /*g_free(uzbl.net.soup_logger);*/
1393 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1394 soup_session_add_feature(uzbl.net.soup_session,
1395 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1398 static WebKitWebSettings*
1400 return webkit_web_view_get_settings(uzbl.gui.web_view);
1405 WebKitWebSettings *ws = view_settings();
1406 if (uzbl.behave.font_size > 0) {
1407 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1410 if (uzbl.behave.monospace_size > 0) {
1411 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1412 uzbl.behave.monospace_size, NULL);
1414 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1415 uzbl.behave.font_size, NULL);
1421 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1425 cmd_disable_plugins() {
1426 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1427 !uzbl.behave.disable_plugins, NULL);
1431 cmd_disable_scripts() {
1432 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1433 !uzbl.behave.disable_scripts, NULL);
1437 cmd_minimum_font_size() {
1438 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1439 uzbl.behave.minimum_font_size, NULL);
1442 cmd_autoload_img() {
1443 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1444 uzbl.behave.autoload_img, NULL);
1449 cmd_autoshrink_img() {
1450 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1451 uzbl.behave.autoshrink_img, NULL);
1456 cmd_enable_spellcheck() {
1457 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1458 uzbl.behave.enable_spellcheck, NULL);
1462 cmd_enable_private() {
1463 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1464 uzbl.behave.enable_private, NULL);
1469 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1470 uzbl.behave.print_bg, NULL);
1475 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1476 uzbl.behave.style_uri, NULL);
1480 cmd_resizable_txt() {
1481 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1482 uzbl.behave.resizable_txt, NULL);
1486 cmd_default_encoding() {
1487 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1488 uzbl.behave.default_encoding, NULL);
1492 cmd_enforce_96dpi() {
1493 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1494 uzbl.behave.enforce_96dpi, NULL);
1498 cmd_caret_browsing() {
1499 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1500 uzbl.behave.caret_browsing, NULL);
1504 cmd_cookie_handler() {
1505 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1506 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1507 if ((g_strcmp0(split[0], "sh") == 0) ||
1508 (g_strcmp0(split[0], "spawn") == 0)) {
1509 g_free (uzbl.behave.cookie_handler);
1510 uzbl.behave.cookie_handler =
1511 g_strdup_printf("sync_%s %s", split[0], split[1]);
1518 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1523 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1528 if(uzbl.behave.inject_html) {
1529 webkit_web_view_load_html_string (uzbl.gui.web_view,
1530 uzbl.behave.inject_html, NULL);
1539 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1540 uzbl.behave.modmask = 0;
1542 if(uzbl.behave.modkey)
1543 g_free(uzbl.behave.modkey);
1544 uzbl.behave.modkey = buf;
1546 for (i = 0; modkeys[i].key != NULL; i++) {
1547 if (g_strrstr(buf, modkeys[i].key))
1548 uzbl.behave.modmask |= modkeys[i].mask;
1554 if (*uzbl.net.useragent == ' ') {
1555 g_free (uzbl.net.useragent);
1556 uzbl.net.useragent = NULL;
1558 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1560 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1561 g_free(uzbl.net.useragent);
1562 uzbl.net.useragent = ua;
1568 gtk_widget_ref(uzbl.gui.scrolled_win);
1569 gtk_widget_ref(uzbl.gui.mainbar);
1570 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1571 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1573 if(uzbl.behave.status_top) {
1574 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1575 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1578 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1579 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1581 gtk_widget_unref(uzbl.gui.scrolled_win);
1582 gtk_widget_unref(uzbl.gui.mainbar);
1583 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1588 set_var_value(gchar *name, gchar *val) {
1589 uzbl_cmdprop *c = NULL;
1593 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1594 /* check for the variable type */
1595 if (c->type == TYPE_STR) {
1596 buf = expand_vars(val);
1599 } else if(c->type == TYPE_INT) {
1600 int *ip = (int *)c->ptr;
1601 buf = expand_vars(val);
1602 *ip = (int)strtoul(buf, &endp, 10);
1604 } else if (c->type == TYPE_FLOAT) {
1605 float *fp = (float *)c->ptr;
1606 buf = expand_vars(val);
1607 *fp = strtod(buf, &endp);
1611 /* invoke a command specific function */
1612 if(c->func) c->func();
1619 Behaviour *b = &uzbl.behave;
1621 if(b->html_buffer->str) {
1622 webkit_web_view_load_html_string (uzbl.gui.web_view,
1623 b->html_buffer->str, b->base_url);
1624 g_string_free(b->html_buffer, TRUE);
1625 b->html_buffer = g_string_new("");
1629 enum {M_CMD, M_HTML};
1631 parse_cmd_line(const char *ctl_line, GString *result) {
1632 Behaviour *b = &uzbl.behave;
1635 if(b->mode == M_HTML) {
1636 len = strlen(b->html_endmarker);
1637 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1638 if(len == strlen(ctl_line)-1 &&
1639 !strncmp(b->html_endmarker, ctl_line, len)) {
1641 set_var_value("mode", "0");
1646 set_timeout(b->html_timeout);
1647 g_string_append(b->html_buffer, ctl_line);
1650 else if((ctl_line[0] == '#') /* Comments */
1651 || (ctl_line[0] == ' ')
1652 || (ctl_line[0] == '\n'))
1653 ; /* ignore these lines */
1654 else { /* parse a command */
1656 gchar **tokens = NULL;
1657 len = strlen(ctl_line);
1659 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1660 ctlstrip = g_strndup(ctl_line, len - 1);
1661 else ctlstrip = g_strdup(ctl_line);
1663 tokens = g_strsplit(ctlstrip, " ", 2);
1664 parse_command(tokens[0], tokens[1], result);
1671 build_stream_name(int type, const gchar* dir) {
1673 State *s = &uzbl.state;
1676 xwin_str = itos((int)uzbl.xwin);
1678 str = g_strdup_printf
1679 ("%s/uzbl_fifo_%s", dir,
1680 s->instance_name ? s->instance_name : xwin_str);
1681 } else if (type == SOCKET) {
1682 str = g_strdup_printf
1683 ("%s/uzbl_socket_%s", dir,
1684 s->instance_name ? s->instance_name : xwin_str );
1691 control_fifo(GIOChannel *gio, GIOCondition condition) {
1692 if (uzbl.state.verbose)
1693 printf("triggered\n");
1698 if (condition & G_IO_HUP)
1699 g_error ("Fifo: Read end of pipe died!\n");
1702 g_error ("Fifo: GIOChannel broke\n");
1704 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1705 if (ret == G_IO_STATUS_ERROR) {
1706 g_error ("Fifo: Error reading: %s\n", err->message);
1710 parse_cmd_line(ctl_line, NULL);
1717 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1718 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1719 if (unlink(uzbl.comm.fifo_path) == -1)
1720 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1721 g_free(uzbl.comm.fifo_path);
1722 uzbl.comm.fifo_path = NULL;
1725 if (*dir == ' ') { /* space unsets the variable */
1730 GIOChannel *chan = NULL;
1731 GError *error = NULL;
1732 gchar *path = build_stream_name(FIFO, dir);
1734 if (!file_exists(path)) {
1735 if (mkfifo (path, 0666) == 0) {
1736 // 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.
1737 chan = g_io_channel_new_file(path, "r+", &error);
1739 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1740 if (uzbl.state.verbose)
1741 printf ("init_fifo: created successfully as %s\n", path);
1742 uzbl.comm.fifo_path = path;
1744 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1745 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1746 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1747 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1749 /* if we got this far, there was an error; cleanup */
1750 if (error) g_error_free (error);
1757 control_stdin(GIOChannel *gio, GIOCondition condition) {
1759 gchar *ctl_line = NULL;
1762 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1763 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1766 parse_cmd_line(ctl_line, NULL);
1774 GIOChannel *chan = NULL;
1775 GError *error = NULL;
1777 chan = g_io_channel_unix_new(fileno(stdin));
1779 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1780 g_error ("Stdin: could not add watch\n");
1782 if (uzbl.state.verbose)
1783 printf ("Stdin: watch added successfully\n");
1786 g_error ("Stdin: Error while opening: %s\n", error->message);
1788 if (error) g_error_free (error);
1792 control_socket(GIOChannel *chan) {
1793 struct sockaddr_un remote;
1794 unsigned int t = sizeof(remote);
1796 GIOChannel *clientchan;
1798 clientsock = accept (g_io_channel_unix_get_fd(chan),
1799 (struct sockaddr *) &remote, &t);
1801 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1802 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1803 (GIOFunc) control_client_socket, clientchan);
1810 control_client_socket(GIOChannel *clientchan) {
1812 GString *result = g_string_new("");
1813 GError *error = NULL;
1817 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1818 if (ret == G_IO_STATUS_ERROR) {
1819 g_warning ("Error reading: %s\n", error->message);
1820 g_io_channel_shutdown(clientchan, TRUE, &error);
1822 } else if (ret == G_IO_STATUS_EOF) {
1823 /* shutdown and remove channel watch from main loop */
1824 g_io_channel_shutdown(clientchan, TRUE, &error);
1829 parse_cmd_line (ctl_line, result);
1830 g_string_append_c(result, '\n');
1831 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1833 if (ret == G_IO_STATUS_ERROR) {
1834 g_warning ("Error writing: %s", error->message);
1836 g_io_channel_flush(clientchan, &error);
1839 if (error) g_error_free (error);
1840 g_string_free(result, TRUE);
1846 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1847 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1848 if (unlink(uzbl.comm.socket_path) == -1)
1849 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1850 g_free(uzbl.comm.socket_path);
1851 uzbl.comm.socket_path = NULL;
1859 GIOChannel *chan = NULL;
1861 struct sockaddr_un local;
1862 gchar *path = build_stream_name(SOCKET, dir);
1864 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1866 local.sun_family = AF_UNIX;
1867 strcpy (local.sun_path, path);
1868 unlink (local.sun_path);
1870 len = strlen (local.sun_path) + sizeof (local.sun_family);
1871 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1872 if (uzbl.state.verbose)
1873 printf ("init_socket: opened in %s\n", path);
1876 if( (chan = g_io_channel_unix_new(sock)) ) {
1877 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1878 uzbl.comm.socket_path = path;
1881 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1883 /* if we got this far, there was an error; cleanup */
1890 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1891 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1893 // this function may be called very early when the templates are not set (yet), hence the checks
1895 update_title (void) {
1896 Behaviour *b = &uzbl.behave;
1899 if (b->show_status) {
1900 if (b->title_format_short) {
1901 parsed = expand_template(b->title_format_short, FALSE);
1902 if (uzbl.gui.main_window)
1903 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1906 if (b->status_format) {
1907 parsed = expand_template(b->status_format, TRUE);
1908 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1911 if (b->status_background) {
1913 gdk_color_parse (b->status_background, &color);
1914 //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)
1915 if (uzbl.gui.main_window)
1916 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1919 if (b->title_format_long) {
1920 parsed = expand_template(b->title_format_long, FALSE);
1921 if (uzbl.gui.main_window)
1922 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1929 key_press_cb (GtkWidget* window, GdkEventKey* event)
1931 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1935 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1936 || 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)
1939 /* turn off insert mode (if always_insert_mode is not used) */
1940 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1941 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1946 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1949 if (event->keyval == GDK_Escape) {
1950 g_string_truncate(uzbl.state.keycmd, 0);
1952 dehilight(uzbl.gui.web_view, NULL, NULL);
1956 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1957 if (event->keyval == GDK_Insert) {
1959 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1960 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1962 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1965 g_string_append (uzbl.state.keycmd, str);
1972 if (event->keyval == GDK_BackSpace)
1973 keycmd_bs(NULL, NULL, NULL);
1975 gboolean key_ret = FALSE;
1976 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1978 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1980 run_keycmd(key_ret);
1982 if (key_ret) return (!uzbl.behave.insert_mode);
1987 run_keycmd(const gboolean key_ret) {
1988 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1990 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1991 g_string_truncate(uzbl.state.keycmd, 0);
1992 parse_command(act->name, act->param, NULL);
1996 /* try if it's an incremental keycmd or one that takes args, and run it */
1997 GString* short_keys = g_string_new ("");
1998 GString* short_keys_inc = g_string_new ("");
2000 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2001 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2002 g_string_assign(short_keys_inc, short_keys->str);
2003 g_string_append_c(short_keys, '_');
2004 g_string_append_c(short_keys_inc, '*');
2006 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2007 /* run normal cmds only if return was pressed */
2008 exec_paramcmd(act, i);
2009 g_string_truncate(uzbl.state.keycmd, 0);
2011 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2012 if (key_ret) /* just quit the incremental command on return */
2013 g_string_truncate(uzbl.state.keycmd, 0);
2014 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2018 g_string_truncate(short_keys, short_keys->len - 1);
2020 g_string_free (short_keys, TRUE);
2021 g_string_free (short_keys_inc, TRUE);
2025 exec_paramcmd(const Action *act, const guint i) {
2026 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2027 GString *actionname = g_string_new ("");
2028 GString *actionparam = g_string_new ("");
2029 g_string_erase (parampart, 0, i+1);
2031 g_string_printf (actionname, act->name, parampart->str);
2033 g_string_printf (actionparam, act->param, parampart->str);
2034 parse_command(actionname->str, actionparam->str, NULL);
2035 g_string_free(actionname, TRUE);
2036 g_string_free(actionparam, TRUE);
2037 g_string_free(parampart, TRUE);
2045 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2046 //main_window_ref = g_object_ref(scrolled_window);
2047 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
2049 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2050 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2052 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2053 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2054 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2055 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2056 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2057 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2058 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2059 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2060 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2061 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2062 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2064 return scrolled_window;
2071 g->mainbar = gtk_hbox_new (FALSE, 0);
2073 /* keep a reference to the bar so we can re-pack it at runtime*/
2074 //sbar_ref = g_object_ref(g->mainbar);
2076 g->mainbar_label = gtk_label_new ("");
2077 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2078 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2079 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2080 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2081 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2082 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2087 GtkWidget* create_window () {
2088 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2089 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2090 gtk_widget_set_name (window, "Uzbl browser");
2091 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2092 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2098 GtkPlug* create_plug () {
2099 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2100 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2101 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2108 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2110 If actname is one that calls an external command, this function will inject
2111 newargs in front of the user-provided args in that command line. They will
2112 come become after the body of the script (in sh) or after the name of
2113 the command to execute (in spawn).
2114 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2115 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2117 The return value consist of two strings: the action (sh, ...) and its args.
2119 If act is not one that calls an external command, then the given action merely
2122 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2123 gchar *actdup = g_strdup(actname);
2124 g_array_append_val(rets, actdup);
2126 if ((g_strcmp0(actname, "spawn") == 0) ||
2127 (g_strcmp0(actname, "sh") == 0) ||
2128 (g_strcmp0(actname, "sync_spawn") == 0) ||
2129 (g_strcmp0(actname, "sync_sh") == 0)) {
2131 GString *a = g_string_new("");
2132 gchar **spawnparts = split_quoted(origargs, FALSE);
2133 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2134 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2136 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2137 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2139 g_array_append_val(rets, a->str);
2140 g_string_free(a, FALSE);
2141 g_strfreev(spawnparts);
2143 gchar *origdup = g_strdup(origargs);
2144 g_array_append_val(rets, origdup);
2146 return (gchar**)g_array_free(rets, FALSE);
2150 run_handler (const gchar *act, const gchar *args) {
2151 /* Consider this code a temporary hack to make the handlers usable.
2152 In practice, all this splicing, injection, and reconstruction is
2153 inefficient, annoying and hard to manage. Potential pitfalls arise
2154 when the handler specific args 1) are not quoted (the handler
2155 callbacks should take care of this) 2) are quoted but interfere
2156 with the users' own quotation. A more ideal solution is
2157 to refactor parse_command so that it doesn't just take a string
2158 and execute it; rather than that, we should have a function which
2159 returns the argument vector parsed from the string. This vector
2160 could be modified (e.g. insert additional args into it) before
2161 passing it to the next function that actually executes it. Though
2162 it still isn't perfect for chain actions.. will reconsider & re-
2163 factor when I have the time. -duc */
2165 char **parts = g_strsplit(act, " ", 2);
2167 if (g_strcmp0(parts[0], "chain") == 0) {
2168 GString *newargs = g_string_new("");
2169 gchar **chainparts = split_quoted(parts[1], FALSE);
2171 /* for every argument in the chain, inject the handler args
2172 and make sure the new parts are wrapped in quotes */
2173 gchar **cp = chainparts;
2175 gchar *quotless = NULL;
2176 gchar **spliced_quotless = NULL; // sigh -_-;
2177 gchar **inpart = NULL;
2180 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2182 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2183 } else quotless = g_strdup(*cp);
2185 spliced_quotless = g_strsplit(quotless, " ", 2);
2186 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2187 g_strfreev(spliced_quotless);
2189 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2195 parse_command(parts[0], &(newargs->str[1]), NULL);
2196 g_string_free(newargs, TRUE);
2197 g_strfreev(chainparts);
2200 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2201 parse_command(inparts[0], inparts[1], NULL);
2209 add_binding (const gchar *key, const gchar *act) {
2210 char **parts = g_strsplit(act, " ", 2);
2217 if (uzbl.state.verbose)
2218 printf ("Binding %-10s : %s\n", key, act);
2219 action = new_action(parts[0], parts[1]);
2221 if (g_hash_table_remove (uzbl.bindings, key))
2222 g_warning ("Overwriting existing binding for \"%s\"", key);
2223 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2228 get_xdg_var (XDG_Var xdg) {
2229 const gchar* actual_value = getenv (xdg.environmental);
2230 const gchar* home = getenv ("HOME");
2231 gchar* return_value;
2233 if (! actual_value || strcmp (actual_value, "") == 0) {
2234 if (xdg.default_value) {
2235 return_value = str_replace ("~", home, xdg.default_value);
2237 return_value = NULL;
2240 return_value = str_replace("~", home, actual_value);
2243 return return_value;
2247 find_xdg_file (int xdg_type, char* filename) {
2248 /* xdg_type = 0 => config
2249 xdg_type = 1 => data
2250 xdg_type = 2 => cache*/
2252 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2253 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2256 gchar* temporary_string;
2260 if (! file_exists (temporary_file) && xdg_type != 2) {
2261 buf = get_xdg_var (XDG[3 + xdg_type]);
2262 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2265 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2266 g_free (temporary_file);
2267 temporary_file = g_strconcat (temporary_string, filename, NULL);
2271 //g_free (temporary_string); - segfaults.
2273 if (file_exists (temporary_file)) {
2274 return temporary_file;
2281 State *s = &uzbl.state;
2282 Network *n = &uzbl.net;
2284 for (i = 0; default_config[i].command != NULL; i++) {
2285 parse_cmd_line(default_config[i].command, NULL);
2288 if (g_strcmp0(s->config_file, "-") == 0) {
2289 s->config_file = NULL;
2293 if (!s->config_file) {
2294 s->config_file = find_xdg_file (0, "/uzbl/config");
2297 if (s->config_file) {
2298 GArray* lines = read_file_by_line (s->config_file);
2302 while ((line = g_array_index(lines, gchar*, i))) {
2303 parse_cmd_line (line, NULL);
2307 g_array_free (lines, TRUE);
2309 if (uzbl.state.verbose)
2310 printf ("No configuration file loaded.\n");
2313 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2316 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2319 if (!uzbl.behave.cookie_handler)
2322 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2323 GString *s = g_string_new ("");
2324 SoupURI * soup_uri = soup_message_get_uri(msg);
2325 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2326 run_handler(uzbl.behave.cookie_handler, s->str);
2328 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2329 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2330 if ( p != NULL ) *p = '\0';
2331 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2333 if (uzbl.comm.sync_stdout)
2334 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2336 g_string_free(s, TRUE);
2340 save_cookies (SoupMessage *msg, gpointer user_data){
2344 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2345 cookie = soup_cookie_to_set_cookie_header(ck->data);
2346 SoupURI * soup_uri = soup_message_get_uri(msg);
2347 GString *s = g_string_new ("");
2348 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2349 run_handler(uzbl.behave.cookie_handler, s->str);
2351 g_string_free(s, TRUE);
2356 /* --- WEBINSPECTOR --- */
2358 hide_window_cb(GtkWidget *widget, gpointer data) {
2361 gtk_widget_hide(widget);
2364 static WebKitWebView*
2365 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2368 (void) web_inspector;
2369 GtkWidget* scrolled_window;
2370 GtkWidget* new_web_view;
2373 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2374 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2375 G_CALLBACK(hide_window_cb), NULL);
2377 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2378 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2379 gtk_widget_show(g->inspector_window);
2381 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2382 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2383 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2384 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2385 gtk_widget_show(scrolled_window);
2387 new_web_view = webkit_web_view_new();
2388 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2390 return WEBKIT_WEB_VIEW(new_web_view);
2394 inspector_show_window_cb (WebKitWebInspector* inspector){
2396 gtk_widget_show(uzbl.gui.inspector_window);
2400 /* TODO: Add variables and code to make use of these functions */
2402 inspector_close_window_cb (WebKitWebInspector* inspector){
2408 inspector_attach_window_cb (WebKitWebInspector* inspector){
2414 inspector_detach_window_cb (WebKitWebInspector* inspector){
2420 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2426 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2432 set_up_inspector() {
2434 WebKitWebSettings *settings = view_settings();
2435 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2437 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2438 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2439 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2440 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2441 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2442 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2443 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2445 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2449 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2451 uzbl_cmdprop *c = v;
2456 if(c->type == TYPE_STR)
2457 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2458 else if(c->type == TYPE_INT)
2459 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2463 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2467 printf("bind %s = %s %s\n", (char *)k ,
2468 (char *)a->name, a->param?(char *)a->param:"");
2473 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2474 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2479 main (int argc, char* argv[]) {
2480 gtk_init (&argc, &argv);
2481 if (!g_thread_supported ())
2482 g_thread_init (NULL);
2483 uzbl.state.executable_path = g_strdup(argv[0]);
2484 uzbl.state.selected_url = NULL;
2485 uzbl.state.searchtx = NULL;
2487 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2488 g_option_context_add_main_entries (context, entries, NULL);
2489 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2490 g_option_context_parse (context, &argc, &argv, NULL);
2491 g_option_context_free(context);
2493 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2494 gboolean verbose_override = uzbl.state.verbose;
2496 /* initialize hash table */
2497 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2499 uzbl.net.soup_session = webkit_get_default_session();
2500 uzbl.state.keycmd = g_string_new("");
2502 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2503 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2504 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2505 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2506 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2507 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2510 if(uname(&uzbl.state.unameinfo) == -1)
2511 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2513 uzbl.gui.sbar.progress_s = g_strdup("=");
2514 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2515 uzbl.gui.sbar.progress_w = 10;
2517 /* HTML mode defaults*/
2518 uzbl.behave.html_buffer = g_string_new("");
2519 uzbl.behave.html_endmarker = g_strdup(".");
2520 uzbl.behave.html_timeout = 60;
2521 uzbl.behave.base_url = g_strdup("http://invalid");
2523 /* default mode indicators */
2524 uzbl.behave.insert_indicator = g_strdup("I");
2525 uzbl.behave.cmd_indicator = g_strdup("C");
2529 make_var_to_name_hash();
2531 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2533 uzbl.gui.scrolled_win = create_browser();
2536 /* initial packing */
2537 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2538 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2540 if (uzbl.state.socket_id) {
2541 uzbl.gui.plug = create_plug ();
2542 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2543 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2545 uzbl.gui.main_window = create_window ();
2546 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2547 gtk_widget_show_all (uzbl.gui.main_window);
2548 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2551 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2553 if (uzbl.state.verbose) {
2554 printf("Uzbl start location: %s\n", argv[0]);
2555 if (uzbl.state.socket_id)
2556 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2558 printf("window_id %i\n",(int) uzbl.xwin);
2559 printf("pid %i\n", getpid ());
2560 printf("name: %s\n", uzbl.state.instance_name);
2563 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2564 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2565 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2566 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2567 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2571 if (!uzbl.behave.show_status)
2572 gtk_widget_hide(uzbl.gui.mainbar);
2579 if (verbose_override > uzbl.state.verbose)
2580 uzbl.state.verbose = verbose_override;
2583 set_var_value("uri", uri_override);
2584 g_free(uri_override);
2585 } else if (uzbl.state.uri)
2586 cmd_load_uri(uzbl.gui.web_view, NULL);
2591 return EXIT_SUCCESS;
2594 /* vi: set et ts=4: */