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, cmd_cookie_handler)},
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);
1180 cmd_cookie_handler() {
1181 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1182 if ((g_strcmp0(split[0], "sh") == 0) ||
1183 (g_strcmp0(split[0], "spawn") == 0)) {
1184 g_free (uzbl.behave.cookie_handler);
1185 uzbl.behave.cookie_handler =
1186 g_strdup_printf("sync_%s %s", split[0], split[1]);
1193 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1198 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1206 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1207 uzbl.behave.modmask = 0;
1209 if(uzbl.behave.modkey)
1210 g_free(uzbl.behave.modkey);
1211 uzbl.behave.modkey = buf;
1213 for (i = 0; modkeys[i].key != NULL; i++) {
1214 if (g_strrstr(buf, modkeys[i].key))
1215 uzbl.behave.modmask |= modkeys[i].mask;
1221 if (*uzbl.net.useragent == ' ') {
1222 g_free (uzbl.net.useragent);
1223 uzbl.net.useragent = NULL;
1225 gchar *ua = expand_template(uzbl.net.useragent);
1227 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1228 g_free(uzbl.net.useragent);
1229 uzbl.net.useragent = ua;
1235 gtk_widget_ref(uzbl.gui.scrolled_win);
1236 gtk_widget_ref(uzbl.gui.mainbar);
1237 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1238 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1240 if(uzbl.behave.status_top) {
1241 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1242 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1245 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1246 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1248 gtk_widget_unref(uzbl.gui.scrolled_win);
1249 gtk_widget_unref(uzbl.gui.mainbar);
1250 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1255 set_var_value(gchar *name, gchar *val) {
1256 uzbl_cmdprop *c = NULL;
1259 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1260 /* check for the variable type */
1261 if (c->type == TYPE_STR) {
1263 *c->ptr = g_strdup(val);
1264 } else if(c->type == TYPE_INT) {
1265 int *ip = GPOINTER_TO_INT(c->ptr);
1266 *ip = (int)strtoul(val, &endp, 10);
1269 /* invoke a command specific function */
1270 if(c->func) c->func();
1276 runcmd(WebKitWebView* page, GArray *argv) {
1278 parse_cmd_line(argv_idx(argv, 0));
1282 parse_cmd_line(const char *ctl_line) {
1286 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1287 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1288 if(tokens[0][0] == 0) {
1289 gchar* value = parseenv(g_strdup(tokens[2]));
1290 set_var_value(tokens[1], value);
1295 printf("Error in command: %s\n", tokens[0]);
1298 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1299 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1300 if(tokens[0][0] == 0) {
1301 get_var_value(tokens[1]);
1305 printf("Error in command: %s\n", tokens[0]);
1308 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1309 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1310 if(tokens[0][0] == 0) {
1311 gchar* value = parseenv(g_strdup(tokens[2]));
1312 add_binding(tokens[1], value);
1317 printf("Error in command: %s\n", tokens[0]);
1320 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1321 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1322 if(tokens[0][0] == 0) {
1323 parse_command(tokens[1], tokens[2]);
1327 printf("Error in command: %s\n", tokens[0]);
1329 /* KEYCMD command */
1330 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1331 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1332 if(tokens[0][0] == 0) {
1333 /* should incremental commands want each individual "keystroke"
1334 sent in a loop or the whole string in one go like now? */
1335 g_string_assign(uzbl.state.keycmd, tokens[1]);
1337 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1344 else if( (ctl_line[0] == '#')
1345 || (ctl_line[0] == ' ')
1346 || (ctl_line[0] == '\n'))
1347 ; /* ignore these lines */
1349 printf("Command not understood (%s)\n", ctl_line);
1355 build_stream_name(int type, const gchar* dir) {
1357 State *s = &uzbl.state;
1360 xwin_str = itos((int)uzbl.xwin);
1362 str = g_strdup_printf
1363 ("%s/uzbl_fifo_%s", dir,
1364 s->instance_name ? s->instance_name : xwin_str);
1365 } else if (type == SOCKET) {
1366 str = g_strdup_printf
1367 ("%s/uzbl_socket_%s", dir,
1368 s->instance_name ? s->instance_name : xwin_str );
1375 control_fifo(GIOChannel *gio, GIOCondition condition) {
1376 if (uzbl.state.verbose)
1377 printf("triggered\n");
1382 if (condition & G_IO_HUP)
1383 g_error ("Fifo: Read end of pipe died!\n");
1386 g_error ("Fifo: GIOChannel broke\n");
1388 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1389 if (ret == G_IO_STATUS_ERROR) {
1390 g_error ("Fifo: Error reading: %s\n", err->message);
1394 parse_cmd_line(ctl_line);
1401 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1402 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1403 if (unlink(uzbl.comm.fifo_path) == -1)
1404 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1405 g_free(uzbl.comm.fifo_path);
1406 uzbl.comm.fifo_path = NULL;
1409 if (*dir == ' ') { /* space unsets the variable */
1414 GIOChannel *chan = NULL;
1415 GError *error = NULL;
1416 gchar *path = build_stream_name(FIFO, dir);
1418 if (!file_exists(path)) {
1419 if (mkfifo (path, 0666) == 0) {
1420 // 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.
1421 chan = g_io_channel_new_file(path, "r+", &error);
1423 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1424 if (uzbl.state.verbose)
1425 printf ("init_fifo: created successfully as %s\n", path);
1426 uzbl.comm.fifo_path = path;
1428 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1429 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1430 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1431 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1433 /* if we got this far, there was an error; cleanup */
1434 if (error) g_error_free (error);
1441 control_stdin(GIOChannel *gio, GIOCondition condition) {
1443 gchar *ctl_line = NULL;
1446 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1447 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1450 parse_cmd_line(ctl_line);
1458 GIOChannel *chan = NULL;
1459 GError *error = NULL;
1461 chan = g_io_channel_unix_new(fileno(stdin));
1463 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1464 g_error ("Stdin: could not add watch\n");
1466 if (uzbl.state.verbose)
1467 printf ("Stdin: watch added successfully\n");
1470 g_error ("Stdin: Error while opening: %s\n", error->message);
1472 if (error) g_error_free (error);
1476 control_socket(GIOChannel *chan) {
1477 struct sockaddr_un remote;
1478 char buffer[512], *ctl_line;
1480 int sock, clientsock, n, done;
1483 sock = g_io_channel_unix_get_fd(chan);
1485 memset (buffer, 0, sizeof (buffer));
1487 t = sizeof (remote);
1488 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1492 memset (temp, 0, sizeof (temp));
1493 n = recv (clientsock, temp, 128, 0);
1495 buffer[strlen (buffer)] = '\0';
1499 strcat (buffer, temp);
1502 if (strcmp (buffer, "\n") < 0) {
1503 buffer[strlen (buffer) - 1] = '\0';
1505 buffer[strlen (buffer)] = '\0';
1508 ctl_line = g_strdup(buffer);
1509 parse_cmd_line (ctl_line);
1512 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1513 GError *error = NULL;
1516 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1517 if (ret == G_IO_STATUS_ERROR)
1518 g_error ("Error reading: %s\n", error->message);
1520 printf("Got line %s (%u bytes) \n",ctl_line, len);
1522 parse_line(ctl_line);
1530 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1531 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1532 if (unlink(uzbl.comm.socket_path) == -1)
1533 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1534 g_free(uzbl.comm.socket_path);
1535 uzbl.comm.socket_path = NULL;
1543 GIOChannel *chan = NULL;
1545 struct sockaddr_un local;
1546 gchar *path = build_stream_name(SOCKET, dir);
1548 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1550 local.sun_family = AF_UNIX;
1551 strcpy (local.sun_path, path);
1552 unlink (local.sun_path);
1554 len = strlen (local.sun_path) + sizeof (local.sun_family);
1555 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1556 if (uzbl.state.verbose)
1557 printf ("init_socket: opened in %s\n", path);
1560 if( (chan = g_io_channel_unix_new(sock)) ) {
1561 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1562 uzbl.comm.socket_path = path;
1565 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1567 /* if we got this far, there was an error; cleanup */
1574 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1575 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1577 // this function may be called very early when the templates are not set (yet), hence the checks
1579 update_title (void) {
1580 Behaviour *b = &uzbl.behave;
1583 if (b->show_status) {
1584 if (b->title_format_short) {
1585 parsed = expand_template(b->title_format_short);
1586 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1589 if (b->status_format) {
1590 parsed = expand_template(b->status_format);
1591 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1594 if (b->status_background) {
1596 gdk_color_parse (b->status_background, &color);
1597 //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)
1598 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1601 if (b->title_format_long) {
1602 parsed = expand_template(b->title_format_long);
1603 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1610 key_press_cb (GtkWidget* window, GdkEventKey* event)
1612 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1616 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1617 || 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)
1620 /* turn off insert mode (if always_insert_mode is not used) */
1621 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1622 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1627 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1630 if (event->keyval == GDK_Escape) {
1631 g_string_truncate(uzbl.state.keycmd, 0);
1636 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1637 if (event->keyval == GDK_Insert) {
1639 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1640 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1642 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1645 g_string_append (uzbl.state.keycmd, str);
1652 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1653 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1657 gboolean key_ret = FALSE;
1658 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1660 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1662 run_keycmd(key_ret);
1664 if (key_ret) return (!uzbl.behave.insert_mode);
1669 run_keycmd(const gboolean key_ret) {
1670 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1672 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1673 g_string_truncate(uzbl.state.keycmd, 0);
1674 parse_command(action->name, action->param);
1678 /* try if it's an incremental keycmd or one that takes args, and run it */
1679 GString* short_keys = g_string_new ("");
1680 GString* short_keys_inc = g_string_new ("");
1682 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1683 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1684 g_string_assign(short_keys_inc, short_keys->str);
1685 g_string_append_c(short_keys, '_');
1686 g_string_append_c(short_keys_inc, '*');
1688 gboolean exec_now = FALSE;
1689 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1690 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1691 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1692 if (key_ret) { /* just quit the incremental command on return */
1693 g_string_truncate(uzbl.state.keycmd, 0);
1695 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1699 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1700 GString* actionname = g_string_new ("");
1701 GString* actionparam = g_string_new ("");
1702 g_string_erase (parampart, 0, i+1);
1704 g_string_printf (actionname, action->name, parampart->str);
1706 g_string_printf (actionparam, action->param, parampart->str);
1707 parse_command(actionname->str, actionparam->str);
1708 g_string_free (actionname, TRUE);
1709 g_string_free (actionparam, TRUE);
1710 g_string_free (parampart, TRUE);
1712 g_string_truncate(uzbl.state.keycmd, 0);
1716 g_string_truncate(short_keys, short_keys->len - 1);
1718 g_string_free (short_keys, TRUE);
1719 g_string_free (short_keys_inc, TRUE);
1726 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1727 //main_window_ref = g_object_ref(scrolled_window);
1728 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
1730 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1731 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1733 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1734 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1735 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1736 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1737 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1738 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1739 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1740 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1741 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1742 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1744 return scrolled_window;
1751 g->mainbar = gtk_hbox_new (FALSE, 0);
1753 /* keep a reference to the bar so we can re-pack it at runtime*/
1754 //sbar_ref = g_object_ref(g->mainbar);
1756 g->mainbar_label = gtk_label_new ("");
1757 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1758 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1759 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1760 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1761 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1766 GtkWidget* create_window () {
1767 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1768 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1769 gtk_widget_set_name (window, "Uzbl browser");
1770 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1771 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1777 run_handler (const gchar *act, const gchar *args) {
1778 char **parts = g_strsplit(act, " ", 2);
1780 else if ((g_strcmp0(parts[0], "spawn") == 0)
1781 || (g_strcmp0(parts[0], "sh") == 0)
1782 || (g_strcmp0(parts[0], "sync_spawn") == 0)
1783 || (g_strcmp0(parts[0], "sync_sh") == 0)) {
1785 GString *a = g_string_new ("");
1787 spawnparts = split_quoted(parts[1], FALSE);
1788 g_string_append_printf(a, "%s", spawnparts[0]);
1789 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1791 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1792 g_string_append_printf(a, " %s", spawnparts[i]);
1793 parse_command(parts[0], a->str);
1794 g_string_free (a, TRUE);
1795 g_strfreev (spawnparts);
1797 parse_command(parts[0], parts[1]);
1802 add_binding (const gchar *key, const gchar *act) {
1803 char **parts = g_strsplit(act, " ", 2);
1810 if (uzbl.state.verbose)
1811 printf ("Binding %-10s : %s\n", key, act);
1812 action = new_action(parts[0], parts[1]);
1814 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1819 get_xdg_var (XDG_Var xdg) {
1820 const gchar* actual_value = getenv (xdg.environmental);
1821 const gchar* home = getenv ("HOME");
1823 gchar* return_value = str_replace ("~", home, actual_value);
1825 if (! actual_value || strcmp (actual_value, "") == 0) {
1826 if (xdg.default_value) {
1827 return_value = str_replace ("~", home, xdg.default_value);
1829 return_value = NULL;
1832 return return_value;
1836 find_xdg_file (int xdg_type, char* filename) {
1837 /* xdg_type = 0 => config
1838 xdg_type = 1 => data
1839 xdg_type = 2 => cache*/
1841 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1842 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1845 gchar* temporary_string;
1849 if (! file_exists (temporary_file) && xdg_type != 2) {
1850 buf = get_xdg_var (XDG[3 + xdg_type]);
1851 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1854 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1855 g_free (temporary_file);
1856 temporary_file = g_strconcat (temporary_string, filename, NULL);
1860 //g_free (temporary_string); - segfaults.
1862 if (file_exists (temporary_file)) {
1863 return temporary_file;
1870 State *s = &uzbl.state;
1871 Network *n = &uzbl.net;
1873 for (i = 0; default_config[i].command != NULL; i++) {
1874 parse_cmd_line(default_config[i].command);
1877 if (!s->config_file) {
1878 s->config_file = find_xdg_file (0, "/uzbl/config");
1881 if (s->config_file) {
1882 GArray* lines = read_file_by_line (s->config_file);
1886 while ((line = g_array_index(lines, gchar*, i))) {
1887 parse_cmd_line (line);
1891 g_array_free (lines, TRUE);
1893 if (uzbl.state.verbose)
1894 printf ("No configuration file loaded.\n");
1897 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1900 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1903 if (!uzbl.behave.cookie_handler) return;
1905 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1906 GString *s = g_string_new ("");
1907 SoupURI * soup_uri = soup_message_get_uri(msg);
1908 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
1909 run_handler(uzbl.behave.cookie_handler, s->str);
1911 if(uzbl.comm.sync_stdout)
1912 soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
1913 //printf("stdout: %s\n", uzbl.comm.sync_stdout); // debugging
1914 if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1916 g_string_free(s, TRUE);
1920 save_cookies (SoupMessage *msg, gpointer user_data){
1924 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1925 cookie = soup_cookie_to_set_cookie_header(ck->data);
1926 SoupURI * soup_uri = soup_message_get_uri(msg);
1927 GString *s = g_string_new ("");
1928 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
1929 run_handler(uzbl.behave.cookie_handler, s->str);
1931 g_string_free(s, TRUE);
1937 main (int argc, char* argv[]) {
1938 gtk_init (&argc, &argv);
1939 if (!g_thread_supported ())
1940 g_thread_init (NULL);
1941 uzbl.state.executable_path = g_strdup(argv[0]);
1942 uzbl.state.selected_url = NULL;
1943 uzbl.state.searchtx = NULL;
1945 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1946 g_option_context_add_main_entries (context, entries, NULL);
1947 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1948 g_option_context_parse (context, &argc, &argv, NULL);
1949 g_option_context_free(context);
1950 /* initialize hash table */
1951 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1953 uzbl.net.soup_session = webkit_get_default_session();
1954 uzbl.state.keycmd = g_string_new("");
1956 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1957 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1958 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1959 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1961 if(uname(&uzbl.state.unameinfo) == -1)
1962 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1964 uzbl.gui.sbar.progress_s = g_strdup("=");
1965 uzbl.gui.sbar.progress_u = g_strdup("ยท");
1966 uzbl.gui.sbar.progress_w = 10;
1971 make_var_to_name_hash();
1973 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1975 uzbl.gui.scrolled_win = create_browser();
1978 /* initial packing */
1979 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1980 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1982 uzbl.gui.main_window = create_window ();
1983 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1986 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1987 gtk_widget_show_all (uzbl.gui.main_window);
1988 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1990 if (uzbl.state.verbose) {
1991 printf("Uzbl start location: %s\n", argv[0]);
1992 printf("window_id %i\n",(int) uzbl.xwin);
1993 printf("pid %i\n", getpid ());
1994 printf("name: %s\n", uzbl.state.instance_name);
1997 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1998 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1999 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2000 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2001 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2005 if (!uzbl.behave.show_status)
2006 gtk_widget_hide(uzbl.gui.mainbar);
2012 if(uzbl.state.uri) {
2013 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
2014 g_array_append_val(a, uzbl.state.uri);
2015 load_uri (uzbl.gui.web_view, a);
2016 g_array_free (a, TRUE);
2022 return EXIT_SUCCESS;
2025 /* vi: set et ts=4: */