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 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
435 if (uzbl.behave.load_start_handler)
436 run_handler(uzbl.behave.load_start_handler, "");
440 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
443 g_free (uzbl.state.uri);
444 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
445 uzbl.state.uri = g_string_free (newuri, FALSE);
446 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
447 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
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, gboolean escape_markup) {
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;
770 buf = uzbl.state.uri?
771 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
772 g_string_append(ret, buf);
776 g_string_append(ret, uzbl.state.uri?
777 uzbl.state.uri:g_strdup(""));
780 buf = itos(uzbl.gui.sbar.load_progress);
781 g_string_append(ret, buf);
784 case SYM_LOADPRGSBAR:
785 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
786 g_string_append(ret, buf);
791 buf = uzbl.gui.main_title?
792 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
793 g_string_append(ret, buf);
797 g_string_append(ret, uzbl.gui.main_title?
798 uzbl.gui.main_title:g_strdup(""));
800 case SYM_SELECTED_URI:
802 buf = uzbl.state.selected_url?
803 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
804 g_string_append(ret, buf);
808 g_string_append(ret, uzbl.state.selected_url?
809 uzbl.state.selected_url:g_strdup(""));
812 buf = itos(uzbl.xwin);
814 uzbl.state.instance_name?uzbl.state.instance_name:buf);
819 buf = uzbl.state.keycmd->str?
820 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
821 g_string_append(ret, buf);
825 g_string_append(ret, uzbl.state.keycmd->str?
826 uzbl.state.keycmd->str:g_strdup(""));
830 uzbl.behave.insert_mode?"[I]":"[C]");
834 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
838 buf = itos(WEBKIT_MAJOR_VERSION);
839 g_string_append(ret, buf);
843 buf = itos(WEBKIT_MINOR_VERSION);
844 g_string_append(ret, buf);
848 buf = itos(WEBKIT_MICRO_VERSION);
849 g_string_append(ret, buf);
853 g_string_append(ret, uzbl.state.unameinfo.sysname);
856 g_string_append(ret, uzbl.state.unameinfo.nodename);
859 g_string_append(ret, uzbl.state.unameinfo.release);
862 g_string_append(ret, uzbl.state.unameinfo.version);
865 g_string_append(ret, uzbl.state.unameinfo.machine);
868 g_string_append(ret, ARCH);
872 g_string_append(ret, uzbl.state.unameinfo.domainname);
876 g_string_append(ret, COMMIT);
882 else if(token == G_TOKEN_INT) {
883 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
884 g_string_append(ret, buf);
887 else if(token == G_TOKEN_IDENTIFIER) {
888 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
890 else if(token == G_TOKEN_CHAR) {
891 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
895 return g_string_free(ret, FALSE);
897 /* --End Statusbar functions-- */
900 sharg_append(GArray *a, const gchar *str) {
901 const gchar *s = (str ? str : "");
902 g_array_append_val(a, s);
905 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
907 run_command (const gchar *command, const guint npre, const gchar **args,
908 const gboolean sync, char **_stdout) {
909 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
912 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
913 gchar *pid = itos(getpid());
914 gchar *xwin = itos(uzbl.xwin);
916 sharg_append(a, command);
917 for (i = 0; i < npre; i++) /* add n args before the default vars */
918 sharg_append(a, args[i]);
919 sharg_append(a, uzbl.state.config_file);
920 sharg_append(a, pid);
921 sharg_append(a, xwin);
922 sharg_append(a, uzbl.comm.fifo_path);
923 sharg_append(a, uzbl.comm.socket_path);
924 sharg_append(a, uzbl.state.uri);
925 sharg_append(a, uzbl.gui.main_title);
927 for (i = npre; i < g_strv_length((gchar**)args); i++)
928 sharg_append(a, args[i]);
932 if (*_stdout) *_stdout = strfree(*_stdout);
934 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
935 NULL, NULL, _stdout, NULL, NULL, &err);
936 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
937 NULL, NULL, NULL, &err);
939 if (uzbl.state.verbose) {
940 GString *s = g_string_new("spawned:");
941 for (i = 0; i < (a->len); i++) {
942 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
943 g_string_append_printf(s, " %s", qarg);
946 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
947 printf("%s\n", s->str);
948 g_string_free(s, TRUE);
951 g_printerr("error on run_command: %s\n", err->message);
956 g_array_free (a, TRUE);
961 split_quoted(const gchar* src, const gboolean unquote) {
962 /* split on unquoted space, return array of strings;
963 remove a layer of quotes and backslashes if unquote */
964 if (!src) return NULL;
968 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
969 GString *s = g_string_new ("");
973 for (p = src; *p != '\0'; p++) {
974 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
975 else if (*p == '\\') { g_string_append_c(s, *p++);
976 g_string_append_c(s, *p); }
977 else if ((*p == '"') && unquote && !sq) dq = !dq;
978 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
980 else if ((*p == '\'') && unquote && !dq) sq = !sq;
981 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
983 else if ((*p == ' ') && !dq && !sq) {
984 dup = g_strdup(s->str);
985 g_array_append_val(a, dup);
986 g_string_truncate(s, 0);
987 } else g_string_append_c(s, *p);
989 dup = g_strdup(s->str);
990 g_array_append_val(a, dup);
991 ret = (gchar**)a->data;
992 g_array_free (a, FALSE);
993 g_string_free (s, TRUE);
998 spawn(WebKitWebView *web_view, GArray *argv) {
1000 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1001 if (argv_idx(argv, 0))
1002 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1006 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1009 if (argv_idx(argv, 0))
1010 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1011 TRUE, &uzbl.comm.sync_stdout);
1015 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1017 if (!uzbl.behave.shell_cmd) {
1018 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1023 gchar *spacer = g_strdup("");
1024 g_array_insert_val(argv, 1, spacer);
1025 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1027 for (i = 1; i < g_strv_length(cmd); i++)
1028 g_array_prepend_val(argv, cmd[i]);
1030 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1036 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1038 if (!uzbl.behave.shell_cmd) {
1039 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1044 gchar *spacer = g_strdup("");
1045 g_array_insert_val(argv, 1, spacer);
1046 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1048 for (i = 1; i < g_strv_length(cmd); i++)
1049 g_array_prepend_val(argv, cmd[i]);
1051 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1052 TRUE, &uzbl.comm.sync_stdout);
1058 parse_command(const char *cmd, const char *param) {
1061 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1064 gchar **par = split_quoted(param, TRUE);
1065 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1067 if (c[1] == NOSPLIT) { /* don't split */
1068 sharg_append(a, param);
1070 for (i = 0; i < g_strv_length(par); i++)
1071 sharg_append(a, par[i]);
1073 c[0](uzbl.gui.web_view, a);
1075 g_array_free (a, TRUE);
1078 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1081 /* command parser */
1084 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1085 G_REGEX_OPTIMIZE, 0, NULL);
1086 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1087 G_REGEX_OPTIMIZE, 0, NULL);
1088 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1089 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1090 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1091 G_REGEX_OPTIMIZE, 0, NULL);
1092 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1093 G_REGEX_OPTIMIZE, 0, NULL);
1097 get_var_value(gchar *name) {
1100 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1101 if(c->type == TYPE_STR)
1102 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1103 else if(c->type == TYPE_INT)
1104 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1113 if(*uzbl.net.proxy_url == ' '
1114 || uzbl.net.proxy_url == NULL) {
1115 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1116 (GType) SOUP_SESSION_PROXY_URI);
1119 suri = soup_uri_new(uzbl.net.proxy_url);
1120 g_object_set(G_OBJECT(uzbl.net.soup_session),
1121 SOUP_SESSION_PROXY_URI,
1123 soup_uri_free(suri);
1130 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1131 g_array_append_val (a, uzbl.state.uri);
1132 load_uri(uzbl.gui.web_view, a);
1133 g_array_free (a, TRUE);
1137 cmd_always_insert_mode() {
1138 uzbl.behave.insert_mode =
1139 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1145 g_object_set(G_OBJECT(uzbl.net.soup_session),
1146 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1150 cmd_max_conns_host() {
1151 g_object_set(G_OBJECT(uzbl.net.soup_session),
1152 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1157 soup_session_remove_feature
1158 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1159 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1160 /*g_free(uzbl.net.soup_logger);*/
1162 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1163 soup_session_add_feature(uzbl.net.soup_session,
1164 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1169 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1170 if (uzbl.behave.font_size > 0) {
1171 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1174 if (uzbl.behave.monospace_size > 0) {
1175 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1176 uzbl.behave.monospace_size, NULL);
1178 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1179 uzbl.behave.font_size, NULL);
1184 cmd_disable_plugins() {
1185 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1186 g_object_set (G_OBJECT(ws), "enable-plugins", !uzbl.behave.disable_plugins, NULL);
1190 cmd_minimum_font_size() {
1191 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1192 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1196 cmd_cookie_handler() {
1197 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1198 if ((g_strcmp0(split[0], "sh") == 0) ||
1199 (g_strcmp0(split[0], "spawn") == 0)) {
1200 g_free (uzbl.behave.cookie_handler);
1201 uzbl.behave.cookie_handler =
1202 g_strdup_printf("sync_%s %s", split[0], split[1]);
1209 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1214 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1222 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1223 uzbl.behave.modmask = 0;
1225 if(uzbl.behave.modkey)
1226 g_free(uzbl.behave.modkey);
1227 uzbl.behave.modkey = buf;
1229 for (i = 0; modkeys[i].key != NULL; i++) {
1230 if (g_strrstr(buf, modkeys[i].key))
1231 uzbl.behave.modmask |= modkeys[i].mask;
1237 if (*uzbl.net.useragent == ' ') {
1238 g_free (uzbl.net.useragent);
1239 uzbl.net.useragent = NULL;
1241 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1243 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1244 g_free(uzbl.net.useragent);
1245 uzbl.net.useragent = ua;
1251 gtk_widget_ref(uzbl.gui.scrolled_win);
1252 gtk_widget_ref(uzbl.gui.mainbar);
1253 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1254 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1256 if(uzbl.behave.status_top) {
1257 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1258 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1261 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1262 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1264 gtk_widget_unref(uzbl.gui.scrolled_win);
1265 gtk_widget_unref(uzbl.gui.mainbar);
1266 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1271 set_var_value(gchar *name, gchar *val) {
1272 uzbl_cmdprop *c = NULL;
1275 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1276 /* check for the variable type */
1277 if (c->type == TYPE_STR) {
1279 *c->ptr = g_strdup(val);
1280 } else if(c->type == TYPE_INT) {
1281 int *ip = GPOINTER_TO_INT(c->ptr);
1282 *ip = (int)strtoul(val, &endp, 10);
1285 /* invoke a command specific function */
1286 if(c->func) c->func();
1292 runcmd(WebKitWebView* page, GArray *argv) {
1294 parse_cmd_line(argv_idx(argv, 0));
1298 parse_cmd_line(const char *ctl_line) {
1299 gchar **tokens = NULL;
1302 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1303 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1304 if(tokens[0][0] == 0) {
1305 gchar* value = parseenv(g_strdup(tokens[2]));
1306 set_var_value(tokens[1], value);
1310 printf("Error in command: %s\n", tokens[0]);
1313 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1314 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1315 if(tokens[0][0] == 0) {
1316 get_var_value(tokens[1]);
1319 printf("Error in command: %s\n", tokens[0]);
1322 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1323 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1324 if(tokens[0][0] == 0) {
1325 gchar* value = parseenv(g_strdup(tokens[2]));
1326 add_binding(tokens[1], value);
1330 printf("Error in command: %s\n", tokens[0]);
1333 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1334 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1335 if(tokens[0][0] == 0) {
1336 parse_command(tokens[1], tokens[2]);
1339 printf("Error in command: %s\n", tokens[0]);
1341 /* KEYCMD command */
1342 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1343 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1344 if(tokens[0][0] == 0) {
1345 /* should incremental commands want each individual "keystroke"
1346 sent in a loop or the whole string in one go like now? */
1347 g_string_assign(uzbl.state.keycmd, tokens[1]);
1349 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1355 else if( (ctl_line[0] == '#')
1356 || (ctl_line[0] == ' ')
1357 || (ctl_line[0] == '\n'))
1358 ; /* ignore these lines */
1360 printf("Command not understood (%s)\n", ctl_line);
1369 build_stream_name(int type, const gchar* dir) {
1371 State *s = &uzbl.state;
1374 xwin_str = itos((int)uzbl.xwin);
1376 str = g_strdup_printf
1377 ("%s/uzbl_fifo_%s", dir,
1378 s->instance_name ? s->instance_name : xwin_str);
1379 } else if (type == SOCKET) {
1380 str = g_strdup_printf
1381 ("%s/uzbl_socket_%s", dir,
1382 s->instance_name ? s->instance_name : xwin_str );
1389 control_fifo(GIOChannel *gio, GIOCondition condition) {
1390 if (uzbl.state.verbose)
1391 printf("triggered\n");
1396 if (condition & G_IO_HUP)
1397 g_error ("Fifo: Read end of pipe died!\n");
1400 g_error ("Fifo: GIOChannel broke\n");
1402 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1403 if (ret == G_IO_STATUS_ERROR) {
1404 g_error ("Fifo: Error reading: %s\n", err->message);
1408 parse_cmd_line(ctl_line);
1415 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1416 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1417 if (unlink(uzbl.comm.fifo_path) == -1)
1418 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1419 g_free(uzbl.comm.fifo_path);
1420 uzbl.comm.fifo_path = NULL;
1423 if (*dir == ' ') { /* space unsets the variable */
1428 GIOChannel *chan = NULL;
1429 GError *error = NULL;
1430 gchar *path = build_stream_name(FIFO, dir);
1432 if (!file_exists(path)) {
1433 if (mkfifo (path, 0666) == 0) {
1434 // 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.
1435 chan = g_io_channel_new_file(path, "r+", &error);
1437 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1438 if (uzbl.state.verbose)
1439 printf ("init_fifo: created successfully as %s\n", path);
1440 uzbl.comm.fifo_path = path;
1442 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1443 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1444 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1445 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1447 /* if we got this far, there was an error; cleanup */
1448 if (error) g_error_free (error);
1455 control_stdin(GIOChannel *gio, GIOCondition condition) {
1457 gchar *ctl_line = NULL;
1460 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1461 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1464 parse_cmd_line(ctl_line);
1472 GIOChannel *chan = NULL;
1473 GError *error = NULL;
1475 chan = g_io_channel_unix_new(fileno(stdin));
1477 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1478 g_error ("Stdin: could not add watch\n");
1480 if (uzbl.state.verbose)
1481 printf ("Stdin: watch added successfully\n");
1484 g_error ("Stdin: Error while opening: %s\n", error->message);
1486 if (error) g_error_free (error);
1490 control_socket(GIOChannel *chan) {
1491 struct sockaddr_un remote;
1492 char buffer[512], *ctl_line;
1494 int sock, clientsock, n, done;
1497 sock = g_io_channel_unix_get_fd(chan);
1499 memset (buffer, 0, sizeof (buffer));
1501 t = sizeof (remote);
1502 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1506 memset (temp, 0, sizeof (temp));
1507 n = recv (clientsock, temp, 128, 0);
1509 buffer[strlen (buffer)] = '\0';
1513 strcat (buffer, temp);
1516 if (strcmp (buffer, "\n") < 0) {
1517 buffer[strlen (buffer) - 1] = '\0';
1519 buffer[strlen (buffer)] = '\0';
1522 ctl_line = g_strdup(buffer);
1523 parse_cmd_line (ctl_line);
1526 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1527 GError *error = NULL;
1530 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1531 if (ret == G_IO_STATUS_ERROR)
1532 g_error ("Error reading: %s\n", error->message);
1534 printf("Got line %s (%u bytes) \n",ctl_line, len);
1536 parse_line(ctl_line);
1544 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1545 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1546 if (unlink(uzbl.comm.socket_path) == -1)
1547 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1548 g_free(uzbl.comm.socket_path);
1549 uzbl.comm.socket_path = NULL;
1557 GIOChannel *chan = NULL;
1559 struct sockaddr_un local;
1560 gchar *path = build_stream_name(SOCKET, dir);
1562 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1564 local.sun_family = AF_UNIX;
1565 strcpy (local.sun_path, path);
1566 unlink (local.sun_path);
1568 len = strlen (local.sun_path) + sizeof (local.sun_family);
1569 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1570 if (uzbl.state.verbose)
1571 printf ("init_socket: opened in %s\n", path);
1574 if( (chan = g_io_channel_unix_new(sock)) ) {
1575 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1576 uzbl.comm.socket_path = path;
1579 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1581 /* if we got this far, there was an error; cleanup */
1588 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1589 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1591 // this function may be called very early when the templates are not set (yet), hence the checks
1593 update_title (void) {
1594 Behaviour *b = &uzbl.behave;
1597 if (b->show_status) {
1598 if (b->title_format_short) {
1599 parsed = expand_template(b->title_format_short, FALSE);
1600 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1603 if (b->status_format) {
1604 parsed = expand_template(b->status_format, TRUE);
1605 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1608 if (b->status_background) {
1610 gdk_color_parse (b->status_background, &color);
1611 //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)
1612 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1615 if (b->title_format_long) {
1616 parsed = expand_template(b->title_format_long, FALSE);
1617 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1624 key_press_cb (GtkWidget* window, GdkEventKey* event)
1626 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1630 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1631 || 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)
1634 /* turn off insert mode (if always_insert_mode is not used) */
1635 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1636 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1641 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1644 if (event->keyval == GDK_Escape) {
1645 g_string_truncate(uzbl.state.keycmd, 0);
1650 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1651 if (event->keyval == GDK_Insert) {
1653 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1654 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1656 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1659 g_string_append (uzbl.state.keycmd, str);
1666 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1667 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1671 gboolean key_ret = FALSE;
1672 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1674 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1676 run_keycmd(key_ret);
1678 if (key_ret) return (!uzbl.behave.insert_mode);
1683 run_keycmd(const gboolean key_ret) {
1684 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1686 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1687 g_string_truncate(uzbl.state.keycmd, 0);
1688 parse_command(action->name, action->param);
1692 /* try if it's an incremental keycmd or one that takes args, and run it */
1693 GString* short_keys = g_string_new ("");
1694 GString* short_keys_inc = g_string_new ("");
1696 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1697 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1698 g_string_assign(short_keys_inc, short_keys->str);
1699 g_string_append_c(short_keys, '_');
1700 g_string_append_c(short_keys_inc, '*');
1702 gboolean exec_now = FALSE;
1703 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1704 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1705 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1706 if (key_ret) { /* just quit the incremental command on return */
1707 g_string_truncate(uzbl.state.keycmd, 0);
1709 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1713 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1714 GString* actionname = g_string_new ("");
1715 GString* actionparam = g_string_new ("");
1716 g_string_erase (parampart, 0, i+1);
1718 g_string_printf (actionname, action->name, parampart->str);
1720 g_string_printf (actionparam, action->param, parampart->str);
1721 parse_command(actionname->str, actionparam->str);
1722 g_string_free (actionname, TRUE);
1723 g_string_free (actionparam, TRUE);
1724 g_string_free (parampart, TRUE);
1726 g_string_truncate(uzbl.state.keycmd, 0);
1730 g_string_truncate(short_keys, short_keys->len - 1);
1732 g_string_free (short_keys, TRUE);
1733 g_string_free (short_keys_inc, TRUE);
1740 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1741 //main_window_ref = g_object_ref(scrolled_window);
1742 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
1744 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1745 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1747 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1748 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1749 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1750 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1751 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1752 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1753 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1754 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1755 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1756 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1758 return scrolled_window;
1765 g->mainbar = gtk_hbox_new (FALSE, 0);
1767 /* keep a reference to the bar so we can re-pack it at runtime*/
1768 //sbar_ref = g_object_ref(g->mainbar);
1770 g->mainbar_label = gtk_label_new ("");
1771 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1772 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1773 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1774 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1775 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1780 GtkWidget* create_window () {
1781 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1782 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1783 gtk_widget_set_name (window, "Uzbl browser");
1784 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1785 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1791 run_handler (const gchar *act, const gchar *args) {
1792 char **parts = g_strsplit(act, " ", 2);
1794 else if ((g_strcmp0(parts[0], "spawn") == 0)
1795 || (g_strcmp0(parts[0], "sh") == 0)
1796 || (g_strcmp0(parts[0], "sync_spawn") == 0)
1797 || (g_strcmp0(parts[0], "sync_sh") == 0)) {
1799 GString *a = g_string_new ("");
1801 spawnparts = split_quoted(parts[1], FALSE);
1802 g_string_append_printf(a, "%s", spawnparts[0]);
1803 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1805 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1806 g_string_append_printf(a, " %s", spawnparts[i]);
1807 parse_command(parts[0], a->str);
1808 g_string_free (a, TRUE);
1809 g_strfreev (spawnparts);
1811 parse_command(parts[0], parts[1]);
1816 add_binding (const gchar *key, const gchar *act) {
1817 char **parts = g_strsplit(act, " ", 2);
1824 if (uzbl.state.verbose)
1825 printf ("Binding %-10s : %s\n", key, act);
1826 action = new_action(parts[0], parts[1]);
1828 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1833 get_xdg_var (XDG_Var xdg) {
1834 const gchar* actual_value = getenv (xdg.environmental);
1835 const gchar* home = getenv ("HOME");
1837 gchar* return_value = str_replace ("~", home, actual_value);
1839 if (! actual_value || strcmp (actual_value, "") == 0) {
1840 if (xdg.default_value) {
1841 return_value = str_replace ("~", home, xdg.default_value);
1843 return_value = NULL;
1846 return return_value;
1850 find_xdg_file (int xdg_type, char* filename) {
1851 /* xdg_type = 0 => config
1852 xdg_type = 1 => data
1853 xdg_type = 2 => cache*/
1855 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1856 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1859 gchar* temporary_string;
1863 if (! file_exists (temporary_file) && xdg_type != 2) {
1864 buf = get_xdg_var (XDG[3 + xdg_type]);
1865 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1868 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1869 g_free (temporary_file);
1870 temporary_file = g_strconcat (temporary_string, filename, NULL);
1874 //g_free (temporary_string); - segfaults.
1876 if (file_exists (temporary_file)) {
1877 return temporary_file;
1884 State *s = &uzbl.state;
1885 Network *n = &uzbl.net;
1887 for (i = 0; default_config[i].command != NULL; i++) {
1888 parse_cmd_line(default_config[i].command);
1891 if (!s->config_file) {
1892 s->config_file = find_xdg_file (0, "/uzbl/config");
1895 if (s->config_file) {
1896 GArray* lines = read_file_by_line (s->config_file);
1900 while ((line = g_array_index(lines, gchar*, i))) {
1901 parse_cmd_line (line);
1905 g_array_free (lines, TRUE);
1907 if (uzbl.state.verbose)
1908 printf ("No configuration file loaded.\n");
1911 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1914 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1917 if (!uzbl.behave.cookie_handler) return;
1919 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1920 GString *s = g_string_new ("");
1921 SoupURI * soup_uri = soup_message_get_uri(msg);
1922 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
1923 run_handler(uzbl.behave.cookie_handler, s->str);
1925 if(uzbl.comm.sync_stdout)
1926 soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
1927 //printf("stdout: %s\n", uzbl.comm.sync_stdout); // debugging
1928 if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1930 g_string_free(s, TRUE);
1934 save_cookies (SoupMessage *msg, gpointer user_data){
1938 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1939 cookie = soup_cookie_to_set_cookie_header(ck->data);
1940 SoupURI * soup_uri = soup_message_get_uri(msg);
1941 GString *s = g_string_new ("");
1942 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
1943 run_handler(uzbl.behave.cookie_handler, s->str);
1945 g_string_free(s, TRUE);
1951 main (int argc, char* argv[]) {
1952 gtk_init (&argc, &argv);
1953 if (!g_thread_supported ())
1954 g_thread_init (NULL);
1955 uzbl.state.executable_path = g_strdup(argv[0]);
1956 uzbl.state.selected_url = NULL;
1957 uzbl.state.searchtx = NULL;
1959 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1960 g_option_context_add_main_entries (context, entries, NULL);
1961 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1962 g_option_context_parse (context, &argc, &argv, NULL);
1963 g_option_context_free(context);
1964 /* initialize hash table */
1965 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1967 uzbl.net.soup_session = webkit_get_default_session();
1968 uzbl.state.keycmd = g_string_new("");
1970 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1971 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1972 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1973 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1975 if(uname(&uzbl.state.unameinfo) == -1)
1976 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1978 uzbl.gui.sbar.progress_s = g_strdup("=");
1979 uzbl.gui.sbar.progress_u = g_strdup("ยท");
1980 uzbl.gui.sbar.progress_w = 10;
1985 make_var_to_name_hash();
1987 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1989 uzbl.gui.scrolled_win = create_browser();
1992 /* initial packing */
1993 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1994 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1996 uzbl.gui.main_window = create_window ();
1997 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2000 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2001 gtk_widget_show_all (uzbl.gui.main_window);
2002 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2004 if (uzbl.state.verbose) {
2005 printf("Uzbl start location: %s\n", argv[0]);
2006 printf("window_id %i\n",(int) uzbl.xwin);
2007 printf("pid %i\n", getpid ());
2008 printf("name: %s\n", uzbl.state.instance_name);
2011 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2012 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2013 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2014 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2015 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2019 if (!uzbl.behave.show_status)
2020 gtk_widget_hide(uzbl.gui.mainbar);
2026 if(uzbl.state.uri) {
2027 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
2028 g_array_append_val(a, uzbl.state.uri);
2029 load_uri (uzbl.gui.web_view, a);
2030 g_array_free (a, TRUE);
2036 return EXIT_SUCCESS;
2039 /* vi: set et ts=4: */