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
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
52 #include <sys/socket.h>
54 #include <libsoup/soup.h>
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
62 /* commandline arguments (set initial values for the state variables) */
63 static GOptionEntry entries[] =
65 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
66 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
67 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
68 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
69 { NULL, 0, 0, 0, NULL, NULL, NULL }
73 /* associate command names to their properties */
74 typedef const struct {
80 enum {TYPE_INT, TYPE_STR};
82 /* an abbreviation to help keep the table's width humane */
83 #define PTR(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .func = fun }
88 } var_name_to_ptr[] = {
89 /* variable name pointer to variable in code type callback function */
90 /* --------------------------------------------------------------------------------------- */
91 { "uri", PTR(uzbl.state.uri, STR, cmd_load_uri)},
92 { "status_message", PTR(uzbl.gui.sbar.msg, STR, update_title)},
93 { "show_status", PTR(uzbl.behave.show_status, INT, cmd_set_status)},
94 { "status_top", PTR(uzbl.behave.status_top, INT, move_statusbar)},
95 { "status_format", PTR(uzbl.behave.status_format, STR, update_title)},
96 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, update_title)},
97 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, update_title)},
98 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, update_title)},
99 { "status_background", PTR(uzbl.behave.status_background, STR, update_title)},
100 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, update_title)},
101 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, update_title)},
102 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, NULL)},
103 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, cmd_always_insert_mode)},
104 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, NULL)},
105 { "modkey", PTR(uzbl.behave.modkey, STR, cmd_modkey)},
106 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, NULL)},
107 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, NULL)},
108 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, NULL)},
109 { "history_handler", PTR(uzbl.behave.history_handler, STR, NULL)},
110 { "download_handler", PTR(uzbl.behave.download_handler, STR, NULL)},
111 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, NULL)},
112 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, cmd_fifo_dir)},
113 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, cmd_socket_dir)},
114 { "http_debug", PTR(uzbl.behave.http_debug, INT, cmd_http_debug)},
115 { "font_size", PTR(uzbl.behave.font_size, INT, cmd_font_size)},
116 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, cmd_font_size)},
117 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, cmd_minimum_font_size)},
118 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, cmd_disable_plugins)},
119 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, NULL)},
120 { "proxy_url", PTR(uzbl.net.proxy_url, STR, set_proxy_url)},
121 { "max_conns", PTR(uzbl.net.max_conns, INT, cmd_max_conns)},
122 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, cmd_max_conns_host)},
123 { "useragent", PTR(uzbl.net.useragent, STR, cmd_useragent)},
124 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
125 }, *n2v_p = var_name_to_ptr;
131 { "SHIFT", GDK_SHIFT_MASK }, // shift
132 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
133 { "CONTROL", GDK_CONTROL_MASK }, // control
134 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
135 { "MOD2", GDK_MOD2_MASK }, // 5th mod
136 { "MOD3", GDK_MOD3_MASK }, // 6th mod
137 { "MOD4", GDK_MOD4_MASK }, // 7th mod
138 { "MOD5", GDK_MOD5_MASK }, // 8th mod
139 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
140 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
141 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
142 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
143 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
144 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
145 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
146 { "META", GDK_META_MASK }, // meta (since 2.10)
151 /* construct a hash from the var_name_to_ptr array for quick access */
153 make_var_to_name_hash() {
154 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
156 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
162 /* --- UTILITY FUNCTIONS --- */
168 snprintf(tmp, sizeof(tmp), "%i", val);
169 return g_strdup(tmp);
173 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
176 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
179 str_replace (const char* search, const char* replace, const char* string) {
183 buf = g_strsplit (string, search, -1);
184 ret = g_strjoinv (replace, buf);
185 g_strfreev(buf); // somebody said this segfaults
191 read_file_by_line (gchar *path) {
192 GIOChannel *chan = NULL;
193 gchar *readbuf = NULL;
195 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
198 chan = g_io_channel_new_file(path, "r", NULL);
201 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
202 const gchar* val = g_strdup (readbuf);
203 g_array_append_val (lines, val);
208 g_io_channel_unref (chan);
210 fprintf(stderr, "File '%s' not be read.\n", path);
217 gchar* parseenv (char* string) {
218 extern char** environ;
219 gchar* tmpstr = NULL;
223 while (environ[i] != NULL) {
224 gchar** env = g_strsplit (environ[i], "=", 2);
225 gchar* envname = g_strconcat ("$", env[0], NULL);
227 if (g_strrstr (string, envname) != NULL) {
228 tmpstr = g_strdup(string);
230 string = str_replace(envname, env[1], tmpstr);
235 g_strfreev (env); // somebody said this breaks uzbl
243 setup_signal(int signr, sigfunc *shandler) {
244 struct sigaction nh, oh;
246 nh.sa_handler = shandler;
247 sigemptyset(&nh.sa_mask);
250 if(sigaction(signr, &nh, &oh) < 0)
258 if (uzbl.behave.fifo_dir)
259 unlink (uzbl.comm.fifo_path);
260 if (uzbl.behave.socket_dir)
261 unlink (uzbl.comm.socket_path);
263 g_free(uzbl.state.executable_path);
264 g_string_free(uzbl.state.keycmd, TRUE);
265 g_hash_table_destroy(uzbl.bindings);
266 g_hash_table_destroy(uzbl.behave.commands);
270 /* --- SIGNAL HANDLER --- */
273 catch_sigterm(int s) {
279 catch_sigint(int s) {
285 /* --- CALLBACKS --- */
288 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
291 (void) navigation_action;
292 (void) policy_decision;
294 const gchar* uri = webkit_network_request_get_uri (request);
295 if (uzbl.state.verbose)
296 printf("New window requested -> %s \n", uri);
297 new_window_load_uri(uri);
302 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
306 if (uzbl.state.selected_url != NULL) {
307 if (uzbl.state.verbose)
308 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
309 new_window_load_uri(uzbl.state.selected_url);
311 if (uzbl.state.verbose)
312 printf("New web view -> %s\n","Nothing to open, exiting");
318 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
321 if (uzbl.behave.download_handler) {
322 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
323 if (uzbl.state.verbose)
324 printf("Download -> %s\n",uri);
325 /* if urls not escaped, we may have to escape and quote uri before this call */
326 run_handler(uzbl.behave.download_handler, uri);
331 /* scroll a bar in a given direction */
333 scroll (GtkAdjustment* bar, GArray *argv) {
337 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
338 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
339 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
342 static void scroll_begin(WebKitWebView* page, GArray *argv) {
343 (void) page; (void) argv;
344 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
347 static void scroll_end(WebKitWebView* page, GArray *argv) {
348 (void) page; (void) argv;
349 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
350 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
353 static void scroll_vert(WebKitWebView* page, GArray *argv) {
355 scroll(uzbl.gui.bar_v, argv);
358 static void scroll_horz(WebKitWebView* page, GArray *argv) {
360 scroll(uzbl.gui.bar_h, argv);
365 if (!uzbl.behave.show_status) {
366 gtk_widget_hide(uzbl.gui.mainbar);
368 gtk_widget_show(uzbl.gui.mainbar);
374 toggle_status_cb (WebKitWebView* page, GArray *argv) {
378 if (uzbl.behave.show_status) {
379 gtk_widget_hide(uzbl.gui.mainbar);
381 gtk_widget_show(uzbl.gui.mainbar);
383 uzbl.behave.show_status = !uzbl.behave.show_status;
388 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
392 //Set selected_url state variable
393 g_free(uzbl.state.selected_url);
394 uzbl.state.selected_url = NULL;
396 uzbl.state.selected_url = g_strdup(link);
402 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
406 if (uzbl.gui.main_title)
407 g_free (uzbl.gui.main_title);
408 uzbl.gui.main_title = g_strdup (title);
413 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
416 uzbl.gui.sbar.load_progress = progress;
421 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
425 if (uzbl.behave.load_finish_handler)
426 run_handler(uzbl.behave.load_finish_handler, "");
430 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
434 if (uzbl.behave.load_start_handler)
435 run_handler(uzbl.behave.load_start_handler, "");
439 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
442 g_free (uzbl.state.uri);
443 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
444 uzbl.state.uri = g_string_free (newuri, FALSE);
445 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
446 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
449 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
450 if (uzbl.behave.load_commit_handler)
451 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
455 destroy_cb (GtkWidget* widget, gpointer data) {
463 if (uzbl.behave.history_handler) {
465 struct tm * timeinfo;
468 timeinfo = localtime ( &rawtime );
469 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
470 run_handler(uzbl.behave.history_handler, date);
475 /* VIEW funcs (little webkit wrappers) */
476 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
478 VIEWFUNC(reload_bypass_cache)
479 VIEWFUNC(stop_loading)
486 /* -- command to callback/function map for things we cannot attach to any signals */
488 static struct {char *name; Command command[2];} cmdlist[] =
489 { /* key function no_split */
490 { "back", {view_go_back, 0} },
491 { "forward", {view_go_forward, 0} },
492 { "scroll_vert", {scroll_vert, 0} },
493 { "scroll_horz", {scroll_horz, 0} },
494 { "scroll_begin", {scroll_begin, 0} },
495 { "scroll_end", {scroll_end, 0} },
496 { "reload", {view_reload, 0}, },
497 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
498 { "stop", {view_stop_loading, 0}, },
499 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
500 { "zoom_out", {view_zoom_out, 0}, },
501 { "uri", {load_uri, NOSPLIT} },
502 { "js", {run_js, NOSPLIT} },
503 { "script", {run_external_js, 0} },
504 { "toggle_status", {toggle_status_cb, 0} },
505 { "spawn", {spawn, 0} },
506 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
507 { "sh", {spawn_sh, 0} },
508 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
509 { "exit", {close_uzbl, 0} },
510 { "search", {search_forward_text, NOSPLIT} },
511 { "search_reverse", {search_reverse_text, NOSPLIT} },
512 { "toggle_insert_mode", {toggle_insert_mode, 0} },
513 { "runcmd", {runcmd, NOSPLIT} }
520 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
522 for (i = 0; i < LENGTH(cmdlist); i++)
523 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
526 /* -- CORE FUNCTIONS -- */
529 free_action(gpointer act) {
530 Action *action = (Action*)act;
531 g_free(action->name);
533 g_free(action->param);
538 new_action(const gchar *name, const gchar *param) {
539 Action *action = g_new(Action, 1);
541 action->name = g_strdup(name);
543 action->param = g_strdup(param);
545 action->param = NULL;
551 file_exists (const char * filename) {
552 return (access(filename, F_OK) == 0);
556 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
559 if (argv_idx(argv, 0)) {
560 if (strcmp (argv_idx(argv, 0), "0") == 0) {
561 uzbl.behave.insert_mode = FALSE;
563 uzbl.behave.insert_mode = TRUE;
566 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
573 load_uri (WebKitWebView *web_view, GArray *argv) {
574 if (argv_idx(argv, 0)) {
575 GString* newuri = g_string_new (argv_idx(argv, 0));
576 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
577 g_string_prepend (newuri, "http://");
578 /* if we do handle cookies, ask our handler for them */
579 webkit_web_view_load_uri (web_view, newuri->str);
580 g_string_free (newuri, TRUE);
585 run_js (WebKitWebView * web_view, GArray *argv) {
586 if (argv_idx(argv, 0))
587 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
591 run_external_js (WebKitWebView * web_view, GArray *argv) {
592 if (argv_idx(argv, 0)) {
593 GArray* lines = read_file_by_line (argv_idx (argv, 0));
598 while ((line = g_array_index(lines, gchar*, i))) {
600 js = g_strdup (line);
602 gchar* newjs = g_strconcat (js, line, NULL);
609 if (uzbl.state.verbose)
610 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
612 if (argv_idx (argv, 1)) {
613 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
617 webkit_web_view_execute_script (web_view, js);
619 g_array_free (lines, TRUE);
624 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
625 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
626 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
627 webkit_web_view_unmark_text_matches (page);
628 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
629 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
633 if (uzbl.state.searchtx) {
634 if (uzbl.state.verbose)
635 printf ("Searching: %s\n", uzbl.state.searchtx);
636 webkit_web_view_set_highlight_text_matches (page, TRUE);
637 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
642 search_forward_text (WebKitWebView *page, GArray *argv) {
643 search_text(page, argv, TRUE);
647 search_reverse_text (WebKitWebView *page, GArray *argv) {
648 search_text(page, argv, FALSE);
652 new_window_load_uri (const gchar * uri) {
653 GString* to_execute = g_string_new ("");
654 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
656 for (i = 0; entries[i].long_name != NULL; i++) {
657 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
658 gchar** str = (gchar**)entries[i].arg_data;
660 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
664 if (uzbl.state.verbose)
665 printf("\n%s\n", to_execute->str);
666 g_spawn_command_line_async (to_execute->str, NULL);
667 g_string_free (to_execute, TRUE);
671 close_uzbl (WebKitWebView *page, GArray *argv) {
677 /* --Statusbar functions-- */
679 build_progressbar_ascii(int percent) {
680 int width=uzbl.gui.sbar.progress_w;
683 GString *bar = g_string_new("");
685 l = (double)percent*((double)width/100.);
686 l = (int)(l+.5)>=(int)l ? l+.5 : l;
688 for(i=0; i<(int)l; i++)
689 g_string_append(bar, uzbl.gui.sbar.progress_s);
692 g_string_append(bar, uzbl.gui.sbar.progress_u);
694 return g_string_free(bar, FALSE);
699 const GScannerConfig scan_config = {
702 ) /* cset_skip_characters */,
707 ) /* cset_identifier_first */,
714 ) /* cset_identifier_nth */,
715 ( "" ) /* cpair_comment_single */,
717 TRUE /* case_sensitive */,
719 FALSE /* skip_comment_multi */,
720 FALSE /* skip_comment_single */,
721 FALSE /* scan_comment_multi */,
722 TRUE /* scan_identifier */,
723 TRUE /* scan_identifier_1char */,
724 FALSE /* scan_identifier_NULL */,
725 TRUE /* scan_symbols */,
726 FALSE /* scan_binary */,
727 FALSE /* scan_octal */,
728 FALSE /* scan_float */,
729 FALSE /* scan_hex */,
730 FALSE /* scan_hex_dollar */,
731 FALSE /* scan_string_sq */,
732 FALSE /* scan_string_dq */,
733 TRUE /* numbers_2_int */,
734 FALSE /* int_2_float */,
735 FALSE /* identifier_2_string */,
736 FALSE /* char_2_token */,
737 FALSE /* symbol_2_token */,
738 TRUE /* scope_0_fallback */,
743 uzbl.scan = g_scanner_new(&scan_config);
744 while(symp->symbol_name) {
745 g_scanner_scope_add_symbol(uzbl.scan, 0,
747 GINT_TO_POINTER(symp->symbol_token));
753 expand_template(const char *template) {
754 if(!template) return NULL;
756 GTokenType token = G_TOKEN_NONE;
757 GString *ret = g_string_new("");
761 g_scanner_input_text(uzbl.scan, template, strlen(template));
762 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
763 token = g_scanner_get_next_token(uzbl.scan);
765 if(token == G_TOKEN_SYMBOL) {
766 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
769 buf = uzbl.state.uri?
770 g_markup_printf_escaped("%s", uzbl.state.uri) :
772 g_string_append(ret, buf);
776 buf = itos(uzbl.gui.sbar.load_progress);
777 g_string_append(ret, buf);
780 case SYM_LOADPRGSBAR:
781 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
782 g_string_append(ret, buf);
786 buf = uzbl.gui.main_title?
787 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
789 g_string_append(ret, buf);
792 case SYM_SELECTED_URI:
793 buf = uzbl.state.selected_url?
794 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
796 g_string_append(ret, buf);
800 buf = itos(uzbl.xwin);
802 uzbl.state.instance_name?uzbl.state.instance_name:buf);
806 buf = uzbl.state.keycmd->str?
807 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
809 g_string_append(ret, buf);
814 uzbl.behave.insert_mode?"[I]":"[C]");
818 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
822 buf = itos(WEBKIT_MAJOR_VERSION);
823 g_string_append(ret, buf);
827 buf = itos(WEBKIT_MINOR_VERSION);
828 g_string_append(ret, buf);
832 buf = itos(WEBKIT_MICRO_VERSION);
833 g_string_append(ret, buf);
837 g_string_append(ret, uzbl.state.unameinfo.sysname);
840 g_string_append(ret, uzbl.state.unameinfo.nodename);
843 g_string_append(ret, uzbl.state.unameinfo.release);
846 g_string_append(ret, uzbl.state.unameinfo.version);
849 g_string_append(ret, uzbl.state.unameinfo.machine);
852 g_string_append(ret, ARCH);
856 g_string_append(ret, uzbl.state.unameinfo.domainname);
860 g_string_append(ret, COMMIT);
866 else if(token == G_TOKEN_INT) {
867 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
868 g_string_append(ret, buf);
871 else if(token == G_TOKEN_IDENTIFIER) {
872 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
874 else if(token == G_TOKEN_CHAR) {
875 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
879 return g_string_free(ret, FALSE);
881 /* --End Statusbar functions-- */
884 sharg_append(GArray *a, const gchar *str) {
885 const gchar *s = (str ? str : "");
886 g_array_append_val(a, s);
889 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
891 run_command (const gchar *command, const guint npre, const gchar **args,
892 const gboolean sync, char **stdout) {
893 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
896 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
897 gchar *pid = itos(getpid());
898 gchar *xwin = itos(uzbl.xwin);
900 sharg_append(a, command);
901 for (i = 0; i < npre; i++) /* add n args before the default vars */
902 sharg_append(a, args[i]);
903 sharg_append(a, uzbl.state.config_file);
904 sharg_append(a, pid);
905 sharg_append(a, xwin);
906 sharg_append(a, uzbl.comm.fifo_path);
907 sharg_append(a, uzbl.comm.socket_path);
908 sharg_append(a, uzbl.state.uri);
909 sharg_append(a, uzbl.gui.main_title);
911 for (i = npre; i < g_strv_length((gchar**)args); i++)
912 sharg_append(a, args[i]);
916 if (*stdout) *stdout = strfree(*stdout);
918 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
919 NULL, NULL, stdout, NULL, NULL, &err);
920 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
921 NULL, NULL, NULL, &err);
923 if (uzbl.state.verbose) {
924 GString *s = g_string_new("spawned:");
925 for (i = 0; i < (a->len); i++) {
926 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
927 g_string_append_printf(s, " %s", qarg);
930 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
931 printf("%s\n", s->str);
932 g_string_free(s, TRUE);
935 g_printerr("error on run_command: %s\n", err->message);
940 g_array_free (a, TRUE);
945 split_quoted(const gchar* src, const gboolean unquote) {
946 /* split on unquoted space, return array of strings;
947 remove a layer of quotes and backslashes if unquote */
948 if (!src) return NULL;
952 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
953 GString *s = g_string_new ("");
957 for (p = src; *p != '\0'; p++) {
958 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
959 else if (*p == '\\') { g_string_append_c(s, *p++);
960 g_string_append_c(s, *p); }
961 else if ((*p == '"') && unquote && !sq) dq = !dq;
962 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
964 else if ((*p == '\'') && unquote && !dq) sq = !sq;
965 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
967 else if ((*p == ' ') && !dq && !sq) {
968 dup = g_strdup(s->str);
969 g_array_append_val(a, dup);
970 g_string_truncate(s, 0);
971 } else g_string_append_c(s, *p);
973 dup = g_strdup(s->str);
974 g_array_append_val(a, dup);
975 ret = (gchar**)a->data;
976 g_array_free (a, FALSE);
977 g_string_free (s, TRUE);
982 spawn(WebKitWebView *web_view, GArray *argv) {
984 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
985 if (argv_idx(argv, 0))
986 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
990 spawn_sync(WebKitWebView *web_view, GArray *argv) {
993 if (argv_idx(argv, 0))
994 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
995 TRUE, &uzbl.comm.sync_stdout);
999 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1001 if (!uzbl.behave.shell_cmd) {
1002 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1007 gchar *spacer = g_strdup("");
1008 g_array_insert_val(argv, 1, spacer);
1009 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1011 for (i = 1; i < g_strv_length(cmd); i++)
1012 g_array_prepend_val(argv, cmd[i]);
1014 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1020 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1022 if (!uzbl.behave.shell_cmd) {
1023 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1028 gchar *spacer = g_strdup("");
1029 g_array_insert_val(argv, 1, spacer);
1030 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1032 for (i = 1; i < g_strv_length(cmd); i++)
1033 g_array_prepend_val(argv, cmd[i]);
1035 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1036 TRUE, &uzbl.comm.sync_stdout);
1042 parse_command(const char *cmd, const char *param) {
1045 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1048 gchar **par = split_quoted(param, TRUE);
1049 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1051 if (c[1] == NOSPLIT) { /* don't split */
1052 sharg_append(a, param);
1054 for (i = 0; i < g_strv_length(par); i++)
1055 sharg_append(a, par[i]);
1057 c[0](uzbl.gui.web_view, a);
1059 g_array_free (a, TRUE);
1062 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1065 /* command parser */
1068 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1069 G_REGEX_OPTIMIZE, 0, NULL);
1070 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1071 G_REGEX_OPTIMIZE, 0, NULL);
1072 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1073 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1074 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1075 G_REGEX_OPTIMIZE, 0, NULL);
1076 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1077 G_REGEX_OPTIMIZE, 0, NULL);
1081 get_var_value(gchar *name) {
1084 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1085 if(c->type == TYPE_STR)
1086 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1087 else if(c->type == TYPE_INT)
1088 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1097 if(*uzbl.net.proxy_url == ' '
1098 || uzbl.net.proxy_url == NULL) {
1099 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1100 (GType) SOUP_SESSION_PROXY_URI);
1103 suri = soup_uri_new(uzbl.net.proxy_url);
1104 g_object_set(G_OBJECT(uzbl.net.soup_session),
1105 SOUP_SESSION_PROXY_URI,
1107 soup_uri_free(suri);
1114 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1115 g_array_append_val (a, uzbl.state.uri);
1116 load_uri(uzbl.gui.web_view, a);
1117 g_array_free (a, TRUE);
1121 cmd_always_insert_mode() {
1122 uzbl.behave.insert_mode =
1123 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1129 g_object_set(G_OBJECT(uzbl.net.soup_session),
1130 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1134 cmd_max_conns_host() {
1135 g_object_set(G_OBJECT(uzbl.net.soup_session),
1136 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1141 soup_session_remove_feature
1142 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1143 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1144 /*g_free(uzbl.net.soup_logger);*/
1146 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1147 soup_session_add_feature(uzbl.net.soup_session,
1148 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1153 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1154 if (uzbl.behave.font_size > 0) {
1155 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1158 if (uzbl.behave.monospace_size > 0) {
1159 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1160 uzbl.behave.monospace_size, NULL);
1162 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1163 uzbl.behave.font_size, NULL);
1168 cmd_disable_plugins() {
1169 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1170 g_object_set (G_OBJECT(ws), "enable-plugins", !uzbl.behave.disable_plugins, NULL);
1174 cmd_minimum_font_size() {
1175 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1176 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1181 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1186 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1194 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1195 uzbl.behave.modmask = 0;
1197 if(uzbl.behave.modkey)
1198 g_free(uzbl.behave.modkey);
1199 uzbl.behave.modkey = buf;
1201 for (i = 0; modkeys[i].key != NULL; i++) {
1202 if (g_strrstr(buf, modkeys[i].key))
1203 uzbl.behave.modmask |= modkeys[i].mask;
1209 if (*uzbl.net.useragent == ' ') {
1210 g_free (uzbl.net.useragent);
1211 uzbl.net.useragent = NULL;
1213 gchar *ua = expand_template(uzbl.net.useragent);
1215 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1216 g_free(uzbl.net.useragent);
1217 uzbl.net.useragent = ua;
1223 gtk_widget_ref(uzbl.gui.scrolled_win);
1224 gtk_widget_ref(uzbl.gui.mainbar);
1225 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1226 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1228 if(uzbl.behave.status_top) {
1229 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1230 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1233 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1234 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1236 gtk_widget_unref(uzbl.gui.scrolled_win);
1237 gtk_widget_unref(uzbl.gui.mainbar);
1238 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1243 set_var_value(gchar *name, gchar *val) {
1244 uzbl_cmdprop *c = NULL;
1247 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1248 /* check for the variable type */
1249 if (c->type == TYPE_STR) {
1251 *c->ptr = g_strdup(val);
1252 } else if(c->type == TYPE_INT) {
1253 int *ip = GPOINTER_TO_INT(c->ptr);
1254 *ip = (int)strtoul(val, &endp, 10);
1257 /* invoke a command specific function */
1258 if(c->func) c->func();
1264 runcmd(WebKitWebView* page, GArray *argv) {
1266 parse_cmd_line(argv_idx(argv, 0));
1270 parse_cmd_line(const char *ctl_line) {
1274 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1275 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1276 if(tokens[0][0] == 0) {
1277 gchar* value = parseenv(g_strdup(tokens[2]));
1278 set_var_value(tokens[1], value);
1283 printf("Error in command: %s\n", tokens[0]);
1286 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1287 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1288 if(tokens[0][0] == 0) {
1289 get_var_value(tokens[1]);
1293 printf("Error in command: %s\n", tokens[0]);
1296 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1297 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1298 if(tokens[0][0] == 0) {
1299 gchar* value = parseenv(g_strdup(tokens[2]));
1300 add_binding(tokens[1], value);
1305 printf("Error in command: %s\n", tokens[0]);
1308 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1309 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1310 if(tokens[0][0] == 0) {
1311 parse_command(tokens[1], tokens[2]);
1315 printf("Error in command: %s\n", tokens[0]);
1317 /* KEYCMD command */
1318 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1319 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1320 if(tokens[0][0] == 0) {
1321 /* should incremental commands want each individual "keystroke"
1322 sent in a loop or the whole string in one go like now? */
1323 g_string_assign(uzbl.state.keycmd, tokens[1]);
1325 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1332 else if( (ctl_line[0] == '#')
1333 || (ctl_line[0] == ' ')
1334 || (ctl_line[0] == '\n'))
1335 ; /* ignore these lines */
1337 printf("Command not understood (%s)\n", ctl_line);
1343 build_stream_name(int type, const gchar* dir) {
1345 State *s = &uzbl.state;
1348 xwin_str = itos((int)uzbl.xwin);
1350 str = g_strdup_printf
1351 ("%s/uzbl_fifo_%s", dir,
1352 s->instance_name ? s->instance_name : xwin_str);
1353 } else if (type == SOCKET) {
1354 str = g_strdup_printf
1355 ("%s/uzbl_socket_%s", dir,
1356 s->instance_name ? s->instance_name : xwin_str );
1363 control_fifo(GIOChannel *gio, GIOCondition condition) {
1364 if (uzbl.state.verbose)
1365 printf("triggered\n");
1370 if (condition & G_IO_HUP)
1371 g_error ("Fifo: Read end of pipe died!\n");
1374 g_error ("Fifo: GIOChannel broke\n");
1376 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1377 if (ret == G_IO_STATUS_ERROR) {
1378 g_error ("Fifo: Error reading: %s\n", err->message);
1382 parse_cmd_line(ctl_line);
1389 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1390 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1391 if (unlink(uzbl.comm.fifo_path) == -1)
1392 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1393 g_free(uzbl.comm.fifo_path);
1394 uzbl.comm.fifo_path = NULL;
1397 if (*dir == ' ') { /* space unsets the variable */
1402 GIOChannel *chan = NULL;
1403 GError *error = NULL;
1404 gchar *path = build_stream_name(FIFO, dir);
1406 if (!file_exists(path)) {
1407 if (mkfifo (path, 0666) == 0) {
1408 // 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.
1409 chan = g_io_channel_new_file(path, "r+", &error);
1411 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1412 if (uzbl.state.verbose)
1413 printf ("init_fifo: created successfully as %s\n", path);
1414 uzbl.comm.fifo_path = path;
1416 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1417 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1418 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1419 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1421 /* if we got this far, there was an error; cleanup */
1422 if (error) g_error_free (error);
1429 control_stdin(GIOChannel *gio, GIOCondition condition) {
1431 gchar *ctl_line = NULL;
1434 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1435 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1438 parse_cmd_line(ctl_line);
1446 GIOChannel *chan = NULL;
1447 GError *error = NULL;
1449 chan = g_io_channel_unix_new(fileno(stdin));
1451 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1452 g_error ("Stdin: could not add watch\n");
1454 if (uzbl.state.verbose)
1455 printf ("Stdin: watch added successfully\n");
1458 g_error ("Stdin: Error while opening: %s\n", error->message);
1460 if (error) g_error_free (error);
1464 control_socket(GIOChannel *chan) {
1465 struct sockaddr_un remote;
1466 char buffer[512], *ctl_line;
1468 int sock, clientsock, n, done;
1471 sock = g_io_channel_unix_get_fd(chan);
1473 memset (buffer, 0, sizeof (buffer));
1475 t = sizeof (remote);
1476 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1480 memset (temp, 0, sizeof (temp));
1481 n = recv (clientsock, temp, 128, 0);
1483 buffer[strlen (buffer)] = '\0';
1487 strcat (buffer, temp);
1490 if (strcmp (buffer, "\n") < 0) {
1491 buffer[strlen (buffer) - 1] = '\0';
1493 buffer[strlen (buffer)] = '\0';
1496 ctl_line = g_strdup(buffer);
1497 parse_cmd_line (ctl_line);
1500 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1501 GError *error = NULL;
1504 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1505 if (ret == G_IO_STATUS_ERROR)
1506 g_error ("Error reading: %s\n", error->message);
1508 printf("Got line %s (%u bytes) \n",ctl_line, len);
1510 parse_line(ctl_line);
1518 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1519 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1520 if (unlink(uzbl.comm.socket_path) == -1)
1521 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1522 g_free(uzbl.comm.socket_path);
1523 uzbl.comm.socket_path = NULL;
1531 GIOChannel *chan = NULL;
1533 struct sockaddr_un local;
1534 gchar *path = build_stream_name(SOCKET, dir);
1536 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1538 local.sun_family = AF_UNIX;
1539 strcpy (local.sun_path, path);
1540 unlink (local.sun_path);
1542 len = strlen (local.sun_path) + sizeof (local.sun_family);
1543 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1544 if (uzbl.state.verbose)
1545 printf ("init_socket: opened in %s\n", path);
1548 if( (chan = g_io_channel_unix_new(sock)) ) {
1549 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1550 uzbl.comm.socket_path = path;
1553 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1555 /* if we got this far, there was an error; cleanup */
1562 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1563 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1565 // this function may be called very early when the templates are not set (yet), hence the checks
1567 update_title (void) {
1568 Behaviour *b = &uzbl.behave;
1571 if (b->show_status) {
1572 if (b->title_format_short) {
1573 parsed = expand_template(b->title_format_short);
1574 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1577 if (b->status_format) {
1578 parsed = expand_template(b->status_format);
1579 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1582 if (b->status_background) {
1584 gdk_color_parse (b->status_background, &color);
1585 //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)
1586 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1589 if (b->title_format_long) {
1590 parsed = expand_template(b->title_format_long);
1591 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1598 key_press_cb (GtkWidget* window, GdkEventKey* event)
1600 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1604 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1605 || 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)
1608 /* turn off insert mode (if always_insert_mode is not used) */
1609 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1610 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1615 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1618 if (event->keyval == GDK_Escape) {
1619 g_string_truncate(uzbl.state.keycmd, 0);
1624 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1625 if (event->keyval == GDK_Insert) {
1627 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1628 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1630 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1633 g_string_append (uzbl.state.keycmd, str);
1640 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1641 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1645 gboolean key_ret = FALSE;
1646 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1648 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1650 run_keycmd(key_ret);
1652 if (key_ret) return (!uzbl.behave.insert_mode);
1657 run_keycmd(const gboolean key_ret) {
1658 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1660 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1661 g_string_truncate(uzbl.state.keycmd, 0);
1662 parse_command(action->name, action->param);
1666 /* try if it's an incremental keycmd or one that takes args, and run it */
1667 GString* short_keys = g_string_new ("");
1668 GString* short_keys_inc = g_string_new ("");
1670 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1671 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1672 g_string_assign(short_keys_inc, short_keys->str);
1673 g_string_append_c(short_keys, '_');
1674 g_string_append_c(short_keys_inc, '*');
1676 gboolean exec_now = FALSE;
1677 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1678 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1679 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1680 if (key_ret) { /* just quit the incremental command on return */
1681 g_string_truncate(uzbl.state.keycmd, 0);
1683 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1687 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1688 GString* actionname = g_string_new ("");
1689 GString* actionparam = g_string_new ("");
1690 g_string_erase (parampart, 0, i+1);
1692 g_string_printf (actionname, action->name, parampart->str);
1694 g_string_printf (actionparam, action->param, parampart->str);
1695 parse_command(actionname->str, actionparam->str);
1696 g_string_free (actionname, TRUE);
1697 g_string_free (actionparam, TRUE);
1698 g_string_free (parampart, TRUE);
1700 g_string_truncate(uzbl.state.keycmd, 0);
1704 g_string_truncate(short_keys, short_keys->len - 1);
1706 g_string_free (short_keys, TRUE);
1707 g_string_free (short_keys_inc, TRUE);
1714 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1715 //main_window_ref = g_object_ref(scrolled_window);
1716 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
1718 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1719 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1721 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1722 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1723 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1724 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1725 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1726 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1727 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1728 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1729 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1730 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1732 return scrolled_window;
1739 g->mainbar = gtk_hbox_new (FALSE, 0);
1741 /* keep a reference to the bar so we can re-pack it at runtime*/
1742 //sbar_ref = g_object_ref(g->mainbar);
1744 g->mainbar_label = gtk_label_new ("");
1745 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1746 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1747 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1748 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1749 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1754 GtkWidget* create_window () {
1755 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1756 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1757 gtk_widget_set_name (window, "Uzbl browser");
1758 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1759 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1765 run_handler (const gchar *act, const gchar *args) {
1766 char **parts = g_strsplit(act, " ", 2);
1768 else if ((g_strcmp0(parts[0], "spawn") == 0)
1769 || (g_strcmp0(parts[0], "sh") == 0)) {
1771 GString *a = g_string_new ("");
1773 spawnparts = split_quoted(parts[1], FALSE);
1774 g_string_append_printf(a, "%s", spawnparts[0]);
1775 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1776 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1777 g_string_append_printf(a, " %s", spawnparts[i]);
1778 parse_command(parts[0], a->str);
1779 g_string_free (a, TRUE);
1780 g_strfreev (spawnparts);
1782 parse_command(parts[0], parts[1]);
1787 add_binding (const gchar *key, const gchar *act) {
1788 char **parts = g_strsplit(act, " ", 2);
1795 if (uzbl.state.verbose)
1796 printf ("Binding %-10s : %s\n", key, act);
1797 action = new_action(parts[0], parts[1]);
1799 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1804 get_xdg_var (XDG_Var xdg) {
1805 const gchar* actual_value = getenv (xdg.environmental);
1806 const gchar* home = getenv ("HOME");
1808 gchar* return_value = str_replace ("~", home, actual_value);
1810 if (! actual_value || strcmp (actual_value, "") == 0) {
1811 if (xdg.default_value) {
1812 return_value = str_replace ("~", home, xdg.default_value);
1814 return_value = NULL;
1817 return return_value;
1821 find_xdg_file (int xdg_type, char* filename) {
1822 /* xdg_type = 0 => config
1823 xdg_type = 1 => data
1824 xdg_type = 2 => cache*/
1826 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1827 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1830 gchar* temporary_string;
1834 if (! file_exists (temporary_file) && xdg_type != 2) {
1835 buf = get_xdg_var (XDG[3 + xdg_type]);
1836 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1839 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1840 g_free (temporary_file);
1841 temporary_file = g_strconcat (temporary_string, filename, NULL);
1845 //g_free (temporary_string); - segfaults.
1847 if (file_exists (temporary_file)) {
1848 return temporary_file;
1855 State *s = &uzbl.state;
1856 Network *n = &uzbl.net;
1858 for (i = 0; default_config[i].command != NULL; i++) {
1859 parse_cmd_line(default_config[i].command);
1862 if (!s->config_file) {
1863 s->config_file = find_xdg_file (0, "/uzbl/config");
1866 if (s->config_file) {
1867 GArray* lines = read_file_by_line (s->config_file);
1871 while ((line = g_array_index(lines, gchar*, i))) {
1872 parse_cmd_line (line);
1876 g_array_free (lines, TRUE);
1878 if (uzbl.state.verbose)
1879 printf ("No configuration file loaded.\n");
1882 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1885 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1888 if (!uzbl.behave.cookie_handler) return;
1890 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1891 GString *s = g_string_new ("");
1892 SoupURI * soup_uri = soup_message_get_uri(msg);
1893 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
1894 run_handler(uzbl.behave.cookie_handler, s->str);
1896 if(uzbl.comm.sync_stdout)
1897 soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
1898 printf("stdout: %s\n", uzbl.comm.sync_stdout); // debugging
1899 if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1901 g_string_free(s, TRUE);
1905 save_cookies (SoupMessage *msg, gpointer user_data){
1909 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1910 cookie = soup_cookie_to_set_cookie_header(ck->data);
1911 SoupURI * soup_uri = soup_message_get_uri(msg);
1912 GString *s = g_string_new ("");
1913 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
1914 run_handler(uzbl.behave.cookie_handler, s->str);
1916 g_string_free(s, TRUE);
1922 main (int argc, char* argv[]) {
1923 gtk_init (&argc, &argv);
1924 if (!g_thread_supported ())
1925 g_thread_init (NULL);
1926 uzbl.state.executable_path = g_strdup(argv[0]);
1927 uzbl.state.selected_url = NULL;
1928 uzbl.state.searchtx = NULL;
1930 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1931 g_option_context_add_main_entries (context, entries, NULL);
1932 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1933 g_option_context_parse (context, &argc, &argv, NULL);
1934 g_option_context_free(context);
1935 /* initialize hash table */
1936 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1938 uzbl.net.soup_session = webkit_get_default_session();
1939 uzbl.state.keycmd = g_string_new("");
1941 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1942 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1943 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1944 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1946 if(uname(&uzbl.state.unameinfo) == -1)
1947 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1949 uzbl.gui.sbar.progress_s = g_strdup("=");
1950 uzbl.gui.sbar.progress_u = g_strdup("ยท");
1951 uzbl.gui.sbar.progress_w = 10;
1956 make_var_to_name_hash();
1958 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1960 uzbl.gui.scrolled_win = create_browser();
1963 /* initial packing */
1964 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1965 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1967 uzbl.gui.main_window = create_window ();
1968 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1971 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1972 gtk_widget_show_all (uzbl.gui.main_window);
1973 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1975 if (uzbl.state.verbose) {
1976 printf("Uzbl start location: %s\n", argv[0]);
1977 printf("window_id %i\n",(int) uzbl.xwin);
1978 printf("pid %i\n", getpid ());
1979 printf("name: %s\n", uzbl.state.instance_name);
1982 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1983 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1984 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1985 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1986 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1990 if (!uzbl.behave.show_status)
1991 gtk_widget_hide(uzbl.gui.mainbar);
1997 if(uzbl.state.uri) {
1998 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1999 g_array_append_val(a, uzbl.state.uri);
2000 load_uri (uzbl.gui.web_view, a);
2001 g_array_free (a, TRUE);
2007 return EXIT_SUCCESS;
2010 /* vi: set et ts=4: */