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 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, NULL)},
116 { "proxy_url", PTR(uzbl.net.proxy_url, STR, set_proxy_url)},
117 { "max_conns", PTR(uzbl.net.max_conns, INT, cmd_max_conns)},
118 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, cmd_max_conns_host)},
119 { "useragent", PTR(uzbl.net.useragent, STR, cmd_useragent)},
120 /* exported WebKitWebSettings properties*/
121 { "font_size", PTR(uzbl.behave.font_size, INT, cmd_font_size)},
122 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, cmd_font_size)},
123 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, cmd_minimum_font_size)},
124 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, cmd_disable_plugins)},
125 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, cmd_disable_scripts)},
126 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, cmd_autoload_img)},
127 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, cmd_autoshrink_img)},
128 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, cmd_enable_spellcheck)},
129 { "enable_private", PTR(uzbl.behave.enable_private, INT, cmd_enable_private)},
130 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, cmd_print_bg)},
131 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, cmd_style_uri)},
133 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
134 }, *n2v_p = var_name_to_ptr;
140 { "SHIFT", GDK_SHIFT_MASK }, // shift
141 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
142 { "CONTROL", GDK_CONTROL_MASK }, // control
143 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
144 { "MOD2", GDK_MOD2_MASK }, // 5th mod
145 { "MOD3", GDK_MOD3_MASK }, // 6th mod
146 { "MOD4", GDK_MOD4_MASK }, // 7th mod
147 { "MOD5", GDK_MOD5_MASK }, // 8th mod
148 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
149 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
150 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
151 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
152 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
153 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
154 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
155 { "META", GDK_META_MASK }, // meta (since 2.10)
160 /* construct a hash from the var_name_to_ptr array for quick access */
162 make_var_to_name_hash() {
163 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
165 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
171 /* --- UTILITY FUNCTIONS --- */
177 snprintf(tmp, sizeof(tmp), "%i", val);
178 return g_strdup(tmp);
182 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
185 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
188 str_replace (const char* search, const char* replace, const char* string) {
192 buf = g_strsplit (string, search, -1);
193 ret = g_strjoinv (replace, buf);
194 g_strfreev(buf); // somebody said this segfaults
200 read_file_by_line (gchar *path) {
201 GIOChannel *chan = NULL;
202 gchar *readbuf = NULL;
204 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
207 chan = g_io_channel_new_file(path, "r", NULL);
210 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
211 const gchar* val = g_strdup (readbuf);
212 g_array_append_val (lines, val);
217 g_io_channel_unref (chan);
219 fprintf(stderr, "File '%s' not be read.\n", path);
226 gchar* parseenv (char* string) {
227 extern char** environ;
228 gchar* tmpstr = NULL;
232 while (environ[i] != NULL) {
233 gchar** env = g_strsplit (environ[i], "=", 2);
234 gchar* envname = g_strconcat ("$", env[0], NULL);
236 if (g_strrstr (string, envname) != NULL) {
237 tmpstr = g_strdup(string);
239 string = str_replace(envname, env[1], tmpstr);
244 g_strfreev (env); // somebody said this breaks uzbl
252 setup_signal(int signr, sigfunc *shandler) {
253 struct sigaction nh, oh;
255 nh.sa_handler = shandler;
256 sigemptyset(&nh.sa_mask);
259 if(sigaction(signr, &nh, &oh) < 0)
267 if (uzbl.behave.fifo_dir)
268 unlink (uzbl.comm.fifo_path);
269 if (uzbl.behave.socket_dir)
270 unlink (uzbl.comm.socket_path);
272 g_free(uzbl.state.executable_path);
273 g_string_free(uzbl.state.keycmd, TRUE);
274 g_hash_table_destroy(uzbl.bindings);
275 g_hash_table_destroy(uzbl.behave.commands);
279 /* --- SIGNAL HANDLER --- */
282 catch_sigterm(int s) {
288 catch_sigint(int s) {
294 /* --- CALLBACKS --- */
297 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
300 (void) navigation_action;
301 (void) policy_decision;
303 const gchar* uri = webkit_network_request_get_uri (request);
304 if (uzbl.state.verbose)
305 printf("New window requested -> %s \n", uri);
306 new_window_load_uri(uri);
311 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
315 if (uzbl.state.selected_url != NULL) {
316 if (uzbl.state.verbose)
317 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
318 new_window_load_uri(uzbl.state.selected_url);
320 if (uzbl.state.verbose)
321 printf("New web view -> %s\n","Nothing to open, exiting");
327 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
330 if (uzbl.behave.download_handler) {
331 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
332 if (uzbl.state.verbose)
333 printf("Download -> %s\n",uri);
334 /* if urls not escaped, we may have to escape and quote uri before this call */
335 run_handler(uzbl.behave.download_handler, uri);
340 /* scroll a bar in a given direction */
342 scroll (GtkAdjustment* bar, GArray *argv) {
346 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
347 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
348 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
351 static void scroll_begin(WebKitWebView* page, GArray *argv) {
352 (void) page; (void) argv;
353 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
356 static void scroll_end(WebKitWebView* page, GArray *argv) {
357 (void) page; (void) argv;
358 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
359 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
362 static void scroll_vert(WebKitWebView* page, GArray *argv) {
364 scroll(uzbl.gui.bar_v, argv);
367 static void scroll_horz(WebKitWebView* page, GArray *argv) {
369 scroll(uzbl.gui.bar_h, argv);
374 if (!uzbl.behave.show_status) {
375 gtk_widget_hide(uzbl.gui.mainbar);
377 gtk_widget_show(uzbl.gui.mainbar);
383 toggle_status_cb (WebKitWebView* page, GArray *argv) {
387 if (uzbl.behave.show_status) {
388 gtk_widget_hide(uzbl.gui.mainbar);
390 gtk_widget_show(uzbl.gui.mainbar);
392 uzbl.behave.show_status = !uzbl.behave.show_status;
397 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
401 //Set selected_url state variable
402 g_free(uzbl.state.selected_url);
403 uzbl.state.selected_url = NULL;
405 uzbl.state.selected_url = g_strdup(link);
411 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
415 if (uzbl.gui.main_title)
416 g_free (uzbl.gui.main_title);
417 uzbl.gui.main_title = g_strdup (title);
422 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
425 uzbl.gui.sbar.load_progress = progress;
430 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
434 if (uzbl.behave.load_finish_handler)
435 run_handler(uzbl.behave.load_finish_handler, "");
439 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
443 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
444 if (uzbl.behave.load_start_handler)
445 run_handler(uzbl.behave.load_start_handler, "");
449 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
452 g_free (uzbl.state.uri);
453 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
454 uzbl.state.uri = g_string_free (newuri, FALSE);
455 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
456 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
459 if (uzbl.behave.load_commit_handler)
460 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
464 destroy_cb (GtkWidget* widget, gpointer data) {
472 if (uzbl.behave.history_handler) {
474 struct tm * timeinfo;
477 timeinfo = localtime ( &rawtime );
478 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
479 run_handler(uzbl.behave.history_handler, date);
484 /* VIEW funcs (little webkit wrappers) */
485 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
487 VIEWFUNC(reload_bypass_cache)
488 VIEWFUNC(stop_loading)
495 /* -- command to callback/function map for things we cannot attach to any signals */
497 static struct {char *name; Command command[2];} cmdlist[] =
498 { /* key function no_split */
499 { "back", {view_go_back, 0} },
500 { "forward", {view_go_forward, 0} },
501 { "scroll_vert", {scroll_vert, 0} },
502 { "scroll_horz", {scroll_horz, 0} },
503 { "scroll_begin", {scroll_begin, 0} },
504 { "scroll_end", {scroll_end, 0} },
505 { "reload", {view_reload, 0}, },
506 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
507 { "stop", {view_stop_loading, 0}, },
508 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
509 { "zoom_out", {view_zoom_out, 0}, },
510 { "uri", {load_uri, NOSPLIT} },
511 { "js", {run_js, NOSPLIT} },
512 { "script", {run_external_js, 0} },
513 { "toggle_status", {toggle_status_cb, 0} },
514 { "spawn", {spawn, 0} },
515 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
516 { "sh", {spawn_sh, 0} },
517 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
518 { "exit", {close_uzbl, 0} },
519 { "search", {search_forward_text, NOSPLIT} },
520 { "search_reverse", {search_reverse_text, NOSPLIT} },
521 { "toggle_insert_mode", {toggle_insert_mode, 0} },
522 { "runcmd", {runcmd, NOSPLIT} },
523 { "set", {set_var, NOSPLIT} }
530 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
532 for (i = 0; i < LENGTH(cmdlist); i++)
533 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
536 /* -- CORE FUNCTIONS -- */
539 free_action(gpointer act) {
540 Action *action = (Action*)act;
541 g_free(action->name);
543 g_free(action->param);
548 new_action(const gchar *name, const gchar *param) {
549 Action *action = g_new(Action, 1);
551 action->name = g_strdup(name);
553 action->param = g_strdup(param);
555 action->param = NULL;
561 file_exists (const char * filename) {
562 return (access(filename, F_OK) == 0);
566 set_var(WebKitWebView *page, GArray *argv) {
570 ctl_line = g_strdup_printf("%s %s", "set", argv_idx(argv, 0));
571 parse_cmd_line(ctl_line);
576 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
579 if (argv_idx(argv, 0)) {
580 if (strcmp (argv_idx(argv, 0), "0") == 0) {
581 uzbl.behave.insert_mode = FALSE;
583 uzbl.behave.insert_mode = TRUE;
586 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
593 load_uri (WebKitWebView *web_view, GArray *argv) {
594 if (argv_idx(argv, 0)) {
595 GString* newuri = g_string_new (argv_idx(argv, 0));
596 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
597 g_string_prepend (newuri, "http://");
598 /* if we do handle cookies, ask our handler for them */
599 webkit_web_view_load_uri (web_view, newuri->str);
600 g_string_free (newuri, TRUE);
605 run_js (WebKitWebView * web_view, GArray *argv) {
606 if (argv_idx(argv, 0))
607 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
611 run_external_js (WebKitWebView * web_view, GArray *argv) {
612 if (argv_idx(argv, 0)) {
613 GArray* lines = read_file_by_line (argv_idx (argv, 0));
618 while ((line = g_array_index(lines, gchar*, i))) {
620 js = g_strdup (line);
622 gchar* newjs = g_strconcat (js, line, NULL);
629 if (uzbl.state.verbose)
630 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
632 if (argv_idx (argv, 1)) {
633 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
637 webkit_web_view_execute_script (web_view, js);
639 g_array_free (lines, TRUE);
644 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
645 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
646 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
647 webkit_web_view_unmark_text_matches (page);
648 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
649 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
653 if (uzbl.state.searchtx) {
654 if (uzbl.state.verbose)
655 printf ("Searching: %s\n", uzbl.state.searchtx);
656 webkit_web_view_set_highlight_text_matches (page, TRUE);
657 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
662 search_forward_text (WebKitWebView *page, GArray *argv) {
663 search_text(page, argv, TRUE);
667 search_reverse_text (WebKitWebView *page, GArray *argv) {
668 search_text(page, argv, FALSE);
672 new_window_load_uri (const gchar * uri) {
673 GString* to_execute = g_string_new ("");
674 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
676 for (i = 0; entries[i].long_name != NULL; i++) {
677 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
678 gchar** str = (gchar**)entries[i].arg_data;
680 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
684 if (uzbl.state.verbose)
685 printf("\n%s\n", to_execute->str);
686 g_spawn_command_line_async (to_execute->str, NULL);
687 g_string_free (to_execute, TRUE);
691 close_uzbl (WebKitWebView *page, GArray *argv) {
697 /* --Statusbar functions-- */
699 build_progressbar_ascii(int percent) {
700 int width=uzbl.gui.sbar.progress_w;
703 GString *bar = g_string_new("");
705 l = (double)percent*((double)width/100.);
706 l = (int)(l+.5)>=(int)l ? l+.5 : l;
708 for(i=0; i<(int)l; i++)
709 g_string_append(bar, uzbl.gui.sbar.progress_s);
712 g_string_append(bar, uzbl.gui.sbar.progress_u);
714 return g_string_free(bar, FALSE);
719 const GScannerConfig scan_config = {
722 ) /* cset_skip_characters */,
727 ) /* cset_identifier_first */,
734 ) /* cset_identifier_nth */,
735 ( "" ) /* cpair_comment_single */,
737 TRUE /* case_sensitive */,
739 FALSE /* skip_comment_multi */,
740 FALSE /* skip_comment_single */,
741 FALSE /* scan_comment_multi */,
742 TRUE /* scan_identifier */,
743 TRUE /* scan_identifier_1char */,
744 FALSE /* scan_identifier_NULL */,
745 TRUE /* scan_symbols */,
746 FALSE /* scan_binary */,
747 FALSE /* scan_octal */,
748 FALSE /* scan_float */,
749 FALSE /* scan_hex */,
750 FALSE /* scan_hex_dollar */,
751 FALSE /* scan_string_sq */,
752 FALSE /* scan_string_dq */,
753 TRUE /* numbers_2_int */,
754 FALSE /* int_2_float */,
755 FALSE /* identifier_2_string */,
756 FALSE /* char_2_token */,
757 FALSE /* symbol_2_token */,
758 TRUE /* scope_0_fallback */,
763 uzbl.scan = g_scanner_new(&scan_config);
764 while(symp->symbol_name) {
765 g_scanner_scope_add_symbol(uzbl.scan, 0,
767 GINT_TO_POINTER(symp->symbol_token));
773 expand_template(const char *template, gboolean escape_markup) {
774 if(!template) return NULL;
776 GTokenType token = G_TOKEN_NONE;
777 GString *ret = g_string_new("");
781 g_scanner_input_text(uzbl.scan, template, strlen(template));
782 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
783 token = g_scanner_get_next_token(uzbl.scan);
785 if(token == G_TOKEN_SYMBOL) {
786 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
790 buf = uzbl.state.uri?
791 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
792 g_string_append(ret, buf);
796 g_string_append(ret, uzbl.state.uri?
797 uzbl.state.uri:g_strdup(""));
800 buf = itos(uzbl.gui.sbar.load_progress);
801 g_string_append(ret, buf);
804 case SYM_LOADPRGSBAR:
805 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
806 g_string_append(ret, buf);
811 buf = uzbl.gui.main_title?
812 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
813 g_string_append(ret, buf);
817 g_string_append(ret, uzbl.gui.main_title?
818 uzbl.gui.main_title:g_strdup(""));
820 case SYM_SELECTED_URI:
822 buf = uzbl.state.selected_url?
823 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
824 g_string_append(ret, buf);
828 g_string_append(ret, uzbl.state.selected_url?
829 uzbl.state.selected_url:g_strdup(""));
832 buf = itos(uzbl.xwin);
834 uzbl.state.instance_name?uzbl.state.instance_name:buf);
839 buf = uzbl.state.keycmd->str?
840 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
841 g_string_append(ret, buf);
845 g_string_append(ret, uzbl.state.keycmd->str?
846 uzbl.state.keycmd->str:g_strdup(""));
850 uzbl.behave.insert_mode?"[I]":"[C]");
854 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
858 buf = itos(WEBKIT_MAJOR_VERSION);
859 g_string_append(ret, buf);
863 buf = itos(WEBKIT_MINOR_VERSION);
864 g_string_append(ret, buf);
868 buf = itos(WEBKIT_MICRO_VERSION);
869 g_string_append(ret, buf);
873 g_string_append(ret, uzbl.state.unameinfo.sysname);
876 g_string_append(ret, uzbl.state.unameinfo.nodename);
879 g_string_append(ret, uzbl.state.unameinfo.release);
882 g_string_append(ret, uzbl.state.unameinfo.version);
885 g_string_append(ret, uzbl.state.unameinfo.machine);
888 g_string_append(ret, ARCH);
892 g_string_append(ret, uzbl.state.unameinfo.domainname);
896 g_string_append(ret, COMMIT);
902 else if(token == G_TOKEN_INT) {
903 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
904 g_string_append(ret, buf);
907 else if(token == G_TOKEN_IDENTIFIER) {
908 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
910 else if(token == G_TOKEN_CHAR) {
911 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
915 return g_string_free(ret, FALSE);
917 /* --End Statusbar functions-- */
920 sharg_append(GArray *a, const gchar *str) {
921 const gchar *s = (str ? str : "");
922 g_array_append_val(a, s);
925 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
927 run_command (const gchar *command, const guint npre, const gchar **args,
928 const gboolean sync, char **stdout) {
929 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
932 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
933 gchar *pid = itos(getpid());
934 gchar *xwin = itos(uzbl.xwin);
936 sharg_append(a, command);
937 for (i = 0; i < npre; i++) /* add n args before the default vars */
938 sharg_append(a, args[i]);
939 sharg_append(a, uzbl.state.config_file);
940 sharg_append(a, pid);
941 sharg_append(a, xwin);
942 sharg_append(a, uzbl.comm.fifo_path);
943 sharg_append(a, uzbl.comm.socket_path);
944 sharg_append(a, uzbl.state.uri);
945 sharg_append(a, uzbl.gui.main_title);
947 for (i = npre; i < g_strv_length((gchar**)args); i++)
948 sharg_append(a, args[i]);
952 if (*stdout) *stdout = strfree(*stdout);
954 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
955 NULL, NULL, stdout, NULL, NULL, &err);
956 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
957 NULL, NULL, NULL, &err);
959 if (uzbl.state.verbose) {
960 GString *s = g_string_new("spawned:");
961 for (i = 0; i < (a->len); i++) {
962 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
963 g_string_append_printf(s, " %s", qarg);
966 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
967 printf("%s\n", s->str);
968 g_string_free(s, TRUE);
971 g_printerr("error on run_command: %s\n", err->message);
976 g_array_free (a, TRUE);
981 split_quoted(const gchar* src, const gboolean unquote) {
982 /* split on unquoted space, return array of strings;
983 remove a layer of quotes and backslashes if unquote */
984 if (!src) return NULL;
988 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
989 GString *s = g_string_new ("");
993 for (p = src; *p != '\0'; p++) {
994 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
995 else if (*p == '\\') { g_string_append_c(s, *p++);
996 g_string_append_c(s, *p); }
997 else if ((*p == '"') && unquote && !sq) dq = !dq;
998 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1000 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1001 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1003 else if ((*p == ' ') && !dq && !sq) {
1004 dup = g_strdup(s->str);
1005 g_array_append_val(a, dup);
1006 g_string_truncate(s, 0);
1007 } else g_string_append_c(s, *p);
1009 dup = g_strdup(s->str);
1010 g_array_append_val(a, dup);
1011 ret = (gchar**)a->data;
1012 g_array_free (a, FALSE);
1013 g_string_free (s, TRUE);
1018 spawn(WebKitWebView *web_view, GArray *argv) {
1020 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1021 if (argv_idx(argv, 0))
1022 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1026 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1029 if (argv_idx(argv, 0))
1030 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1031 TRUE, &uzbl.comm.sync_stdout);
1035 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1037 if (!uzbl.behave.shell_cmd) {
1038 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1043 gchar *spacer = g_strdup("");
1044 g_array_insert_val(argv, 1, spacer);
1045 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1047 for (i = 1; i < g_strv_length(cmd); i++)
1048 g_array_prepend_val(argv, cmd[i]);
1050 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1056 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1058 if (!uzbl.behave.shell_cmd) {
1059 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1064 gchar *spacer = g_strdup("");
1065 g_array_insert_val(argv, 1, spacer);
1066 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1068 for (i = 1; i < g_strv_length(cmd); i++)
1069 g_array_prepend_val(argv, cmd[i]);
1071 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1072 TRUE, &uzbl.comm.sync_stdout);
1078 parse_command(const char *cmd, const char *param) {
1081 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1084 gchar **par = split_quoted(param, TRUE);
1085 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1087 if (c[1] == NOSPLIT) { /* don't split */
1088 sharg_append(a, param);
1090 for (i = 0; i < g_strv_length(par); i++)
1091 sharg_append(a, par[i]);
1093 c[0](uzbl.gui.web_view, a);
1095 g_array_free (a, TRUE);
1098 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1101 /* command parser */
1104 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1105 G_REGEX_OPTIMIZE, 0, NULL);
1106 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1107 G_REGEX_OPTIMIZE, 0, NULL);
1108 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1109 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1110 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1111 G_REGEX_OPTIMIZE, 0, NULL);
1112 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1113 G_REGEX_OPTIMIZE, 0, NULL);
1117 get_var_value(gchar *name) {
1120 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1121 if(c->type == TYPE_STR)
1122 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1123 else if(c->type == TYPE_INT)
1124 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1133 if(*uzbl.net.proxy_url == ' '
1134 || uzbl.net.proxy_url == NULL) {
1135 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1136 (GType) SOUP_SESSION_PROXY_URI);
1139 suri = soup_uri_new(uzbl.net.proxy_url);
1140 g_object_set(G_OBJECT(uzbl.net.soup_session),
1141 SOUP_SESSION_PROXY_URI,
1143 soup_uri_free(suri);
1150 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1151 g_array_append_val (a, uzbl.state.uri);
1152 load_uri(uzbl.gui.web_view, a);
1153 g_array_free (a, TRUE);
1157 cmd_always_insert_mode() {
1158 uzbl.behave.insert_mode =
1159 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1165 g_object_set(G_OBJECT(uzbl.net.soup_session),
1166 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1170 cmd_max_conns_host() {
1171 g_object_set(G_OBJECT(uzbl.net.soup_session),
1172 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1177 soup_session_remove_feature
1178 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1179 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1180 /*g_free(uzbl.net.soup_logger);*/
1182 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1183 soup_session_add_feature(uzbl.net.soup_session,
1184 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1187 static WebKitWebSettings*
1189 return webkit_web_view_get_settings(uzbl.gui.web_view);
1194 WebKitWebSettings *ws = view_settings();
1195 if (uzbl.behave.font_size > 0) {
1196 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1199 if (uzbl.behave.monospace_size > 0) {
1200 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1201 uzbl.behave.monospace_size, NULL);
1203 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1204 uzbl.behave.font_size, NULL);
1209 cmd_disable_plugins() {
1210 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1211 !uzbl.behave.disable_plugins, NULL);
1215 cmd_disable_scripts() {
1216 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1217 !uzbl.behave.disable_plugins, NULL);
1221 cmd_minimum_font_size() {
1222 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1223 uzbl.behave.minimum_font_size, NULL);
1226 cmd_autoload_img() {
1227 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1228 uzbl.behave.autoload_img, NULL);
1233 cmd_autoshrink_img() {
1234 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1235 uzbl.behave.autoshrink_img, NULL);
1240 cmd_enable_spellcheck() {
1241 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1242 uzbl.behave.enable_spellcheck, NULL);
1246 cmd_enable_private() {
1247 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1248 uzbl.behave.enable_private, NULL);
1253 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1254 uzbl.behave.print_bg, NULL);
1259 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1260 uzbl.behave.style_uri, NULL);
1264 cmd_cookie_handler() {
1265 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1266 if ((g_strcmp0(split[0], "sh") == 0) ||
1267 (g_strcmp0(split[0], "spawn") == 0)) {
1268 g_free (uzbl.behave.cookie_handler);
1269 uzbl.behave.cookie_handler =
1270 g_strdup_printf("sync_%s %s", split[0], split[1]);
1277 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1282 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1290 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1291 uzbl.behave.modmask = 0;
1293 if(uzbl.behave.modkey)
1294 g_free(uzbl.behave.modkey);
1295 uzbl.behave.modkey = buf;
1297 for (i = 0; modkeys[i].key != NULL; i++) {
1298 if (g_strrstr(buf, modkeys[i].key))
1299 uzbl.behave.modmask |= modkeys[i].mask;
1305 if (*uzbl.net.useragent == ' ') {
1306 g_free (uzbl.net.useragent);
1307 uzbl.net.useragent = NULL;
1309 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1311 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1312 g_free(uzbl.net.useragent);
1313 uzbl.net.useragent = ua;
1319 gtk_widget_ref(uzbl.gui.scrolled_win);
1320 gtk_widget_ref(uzbl.gui.mainbar);
1321 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1322 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1324 if(uzbl.behave.status_top) {
1325 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1326 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1329 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1330 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1332 gtk_widget_unref(uzbl.gui.scrolled_win);
1333 gtk_widget_unref(uzbl.gui.mainbar);
1334 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1339 set_var_value(gchar *name, gchar *val) {
1340 uzbl_cmdprop *c = NULL;
1343 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1344 /* check for the variable type */
1345 if (c->type == TYPE_STR) {
1347 *c->ptr = g_strdup(val);
1348 } else if(c->type == TYPE_INT) {
1349 int *ip = GPOINTER_TO_INT(c->ptr);
1350 *ip = (int)strtoul(val, &endp, 10);
1353 /* invoke a command specific function */
1354 if(c->func) c->func();
1360 runcmd(WebKitWebView* page, GArray *argv) {
1362 parse_cmd_line(argv_idx(argv, 0));
1366 parse_cmd_line(const char *ctl_line) {
1367 gchar **tokens = NULL;
1370 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1371 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1372 if(tokens[0][0] == 0) {
1373 gchar* value = parseenv(g_strdup(tokens[2]));
1374 set_var_value(tokens[1], value);
1378 printf("Error in command: %s\n", tokens[0]);
1381 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1382 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1383 if(tokens[0][0] == 0) {
1384 get_var_value(tokens[1]);
1387 printf("Error in command: %s\n", tokens[0]);
1390 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1391 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1392 if(tokens[0][0] == 0) {
1393 gchar* value = parseenv(g_strdup(tokens[2]));
1394 add_binding(tokens[1], value);
1398 printf("Error in command: %s\n", tokens[0]);
1401 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1402 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1403 if(tokens[0][0] == 0) {
1404 parse_command(tokens[1], tokens[2]);
1407 printf("Error in command: %s\n", tokens[0]);
1409 /* KEYCMD command */
1410 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1411 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1412 if(tokens[0][0] == 0) {
1413 /* should incremental commands want each individual "keystroke"
1414 sent in a loop or the whole string in one go like now? */
1415 g_string_assign(uzbl.state.keycmd, tokens[1]);
1417 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1423 else if( (ctl_line[0] == '#')
1424 || (ctl_line[0] == ' ')
1425 || (ctl_line[0] == '\n'))
1426 ; /* ignore these lines */
1428 printf("Command not understood (%s)\n", ctl_line);
1437 build_stream_name(int type, const gchar* dir) {
1439 State *s = &uzbl.state;
1442 xwin_str = itos((int)uzbl.xwin);
1444 str = g_strdup_printf
1445 ("%s/uzbl_fifo_%s", dir,
1446 s->instance_name ? s->instance_name : xwin_str);
1447 } else if (type == SOCKET) {
1448 str = g_strdup_printf
1449 ("%s/uzbl_socket_%s", dir,
1450 s->instance_name ? s->instance_name : xwin_str );
1457 control_fifo(GIOChannel *gio, GIOCondition condition) {
1458 if (uzbl.state.verbose)
1459 printf("triggered\n");
1464 if (condition & G_IO_HUP)
1465 g_error ("Fifo: Read end of pipe died!\n");
1468 g_error ("Fifo: GIOChannel broke\n");
1470 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1471 if (ret == G_IO_STATUS_ERROR) {
1472 g_error ("Fifo: Error reading: %s\n", err->message);
1476 parse_cmd_line(ctl_line);
1483 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1484 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1485 if (unlink(uzbl.comm.fifo_path) == -1)
1486 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1487 g_free(uzbl.comm.fifo_path);
1488 uzbl.comm.fifo_path = NULL;
1491 if (*dir == ' ') { /* space unsets the variable */
1496 GIOChannel *chan = NULL;
1497 GError *error = NULL;
1498 gchar *path = build_stream_name(FIFO, dir);
1500 if (!file_exists(path)) {
1501 if (mkfifo (path, 0666) == 0) {
1502 // 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.
1503 chan = g_io_channel_new_file(path, "r+", &error);
1505 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1506 if (uzbl.state.verbose)
1507 printf ("init_fifo: created successfully as %s\n", path);
1508 uzbl.comm.fifo_path = path;
1510 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1511 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1512 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1513 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1515 /* if we got this far, there was an error; cleanup */
1516 if (error) g_error_free (error);
1523 control_stdin(GIOChannel *gio, GIOCondition condition) {
1525 gchar *ctl_line = NULL;
1528 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1529 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1532 parse_cmd_line(ctl_line);
1540 GIOChannel *chan = NULL;
1541 GError *error = NULL;
1543 chan = g_io_channel_unix_new(fileno(stdin));
1545 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1546 g_error ("Stdin: could not add watch\n");
1548 if (uzbl.state.verbose)
1549 printf ("Stdin: watch added successfully\n");
1552 g_error ("Stdin: Error while opening: %s\n", error->message);
1554 if (error) g_error_free (error);
1558 control_socket(GIOChannel *chan) {
1559 struct sockaddr_un remote;
1560 char buffer[512], *ctl_line;
1562 int sock, clientsock, n, done;
1565 sock = g_io_channel_unix_get_fd(chan);
1567 memset (buffer, 0, sizeof (buffer));
1569 t = sizeof (remote);
1570 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1574 memset (temp, 0, sizeof (temp));
1575 n = recv (clientsock, temp, 128, 0);
1577 buffer[strlen (buffer)] = '\0';
1581 strcat (buffer, temp);
1584 if (strcmp (buffer, "\n") < 0) {
1585 buffer[strlen (buffer) - 1] = '\0';
1587 buffer[strlen (buffer)] = '\0';
1590 ctl_line = g_strdup(buffer);
1591 parse_cmd_line (ctl_line);
1594 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1595 GError *error = NULL;
1598 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1599 if (ret == G_IO_STATUS_ERROR)
1600 g_error ("Error reading: %s\n", error->message);
1602 printf("Got line %s (%u bytes) \n",ctl_line, len);
1604 parse_line(ctl_line);
1612 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1613 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1614 if (unlink(uzbl.comm.socket_path) == -1)
1615 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1616 g_free(uzbl.comm.socket_path);
1617 uzbl.comm.socket_path = NULL;
1625 GIOChannel *chan = NULL;
1627 struct sockaddr_un local;
1628 gchar *path = build_stream_name(SOCKET, dir);
1630 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1632 local.sun_family = AF_UNIX;
1633 strcpy (local.sun_path, path);
1634 unlink (local.sun_path);
1636 len = strlen (local.sun_path) + sizeof (local.sun_family);
1637 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1638 if (uzbl.state.verbose)
1639 printf ("init_socket: opened in %s\n", path);
1642 if( (chan = g_io_channel_unix_new(sock)) ) {
1643 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1644 uzbl.comm.socket_path = path;
1647 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1649 /* if we got this far, there was an error; cleanup */
1656 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1657 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1659 // this function may be called very early when the templates are not set (yet), hence the checks
1661 update_title (void) {
1662 Behaviour *b = &uzbl.behave;
1665 if (b->show_status) {
1666 if (b->title_format_short) {
1667 parsed = expand_template(b->title_format_short, FALSE);
1668 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1671 if (b->status_format) {
1672 parsed = expand_template(b->status_format, TRUE);
1673 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1676 if (b->status_background) {
1678 gdk_color_parse (b->status_background, &color);
1679 //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)
1680 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1683 if (b->title_format_long) {
1684 parsed = expand_template(b->title_format_long, FALSE);
1685 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1692 key_press_cb (GtkWidget* window, GdkEventKey* event)
1694 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1698 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1699 || 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)
1702 /* turn off insert mode (if always_insert_mode is not used) */
1703 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1704 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1709 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1712 if (event->keyval == GDK_Escape) {
1713 g_string_truncate(uzbl.state.keycmd, 0);
1718 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1719 if (event->keyval == GDK_Insert) {
1721 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1722 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1724 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1727 g_string_append (uzbl.state.keycmd, str);
1734 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1735 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1739 gboolean key_ret = FALSE;
1740 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1742 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1744 run_keycmd(key_ret);
1746 if (key_ret) return (!uzbl.behave.insert_mode);
1751 run_keycmd(const gboolean key_ret) {
1752 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1754 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1755 g_string_truncate(uzbl.state.keycmd, 0);
1756 parse_command(action->name, action->param);
1760 /* try if it's an incremental keycmd or one that takes args, and run it */
1761 GString* short_keys = g_string_new ("");
1762 GString* short_keys_inc = g_string_new ("");
1764 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1765 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1766 g_string_assign(short_keys_inc, short_keys->str);
1767 g_string_append_c(short_keys, '_');
1768 g_string_append_c(short_keys_inc, '*');
1770 gboolean exec_now = FALSE;
1771 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1772 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1773 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1774 if (key_ret) { /* just quit the incremental command on return */
1775 g_string_truncate(uzbl.state.keycmd, 0);
1777 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1781 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1782 GString* actionname = g_string_new ("");
1783 GString* actionparam = g_string_new ("");
1784 g_string_erase (parampart, 0, i+1);
1786 g_string_printf (actionname, action->name, parampart->str);
1788 g_string_printf (actionparam, action->param, parampart->str);
1789 parse_command(actionname->str, actionparam->str);
1790 g_string_free (actionname, TRUE);
1791 g_string_free (actionparam, TRUE);
1792 g_string_free (parampart, TRUE);
1794 g_string_truncate(uzbl.state.keycmd, 0);
1798 g_string_truncate(short_keys, short_keys->len - 1);
1800 g_string_free (short_keys, TRUE);
1801 g_string_free (short_keys_inc, TRUE);
1808 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1809 //main_window_ref = g_object_ref(scrolled_window);
1810 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
1812 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1813 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1815 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1816 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1817 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1818 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1819 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1820 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1821 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1822 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1823 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1824 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1826 return scrolled_window;
1833 g->mainbar = gtk_hbox_new (FALSE, 0);
1835 /* keep a reference to the bar so we can re-pack it at runtime*/
1836 //sbar_ref = g_object_ref(g->mainbar);
1838 g->mainbar_label = gtk_label_new ("");
1839 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1840 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1841 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1842 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1843 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1848 GtkWidget* create_window () {
1849 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1850 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1851 gtk_widget_set_name (window, "Uzbl browser");
1852 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1853 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1859 run_handler (const gchar *act, const gchar *args) {
1860 char **parts = g_strsplit(act, " ", 2);
1862 else if ((g_strcmp0(parts[0], "spawn") == 0)
1863 || (g_strcmp0(parts[0], "sh") == 0)
1864 || (g_strcmp0(parts[0], "sync_spawn") == 0)
1865 || (g_strcmp0(parts[0], "sync_sh") == 0)) {
1867 GString *a = g_string_new ("");
1869 spawnparts = split_quoted(parts[1], FALSE);
1870 g_string_append_printf(a, "%s", spawnparts[0]);
1871 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1873 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1874 g_string_append_printf(a, " %s", spawnparts[i]);
1875 parse_command(parts[0], a->str);
1876 g_string_free (a, TRUE);
1877 g_strfreev (spawnparts);
1879 parse_command(parts[0], parts[1]);
1884 add_binding (const gchar *key, const gchar *act) {
1885 char **parts = g_strsplit(act, " ", 2);
1892 if (uzbl.state.verbose)
1893 printf ("Binding %-10s : %s\n", key, act);
1894 action = new_action(parts[0], parts[1]);
1896 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1901 get_xdg_var (XDG_Var xdg) {
1902 const gchar* actual_value = getenv (xdg.environmental);
1903 const gchar* home = getenv ("HOME");
1905 gchar* return_value = str_replace ("~", home, actual_value);
1907 if (! actual_value || strcmp (actual_value, "") == 0) {
1908 if (xdg.default_value) {
1909 return_value = str_replace ("~", home, xdg.default_value);
1911 return_value = NULL;
1914 return return_value;
1918 find_xdg_file (int xdg_type, char* filename) {
1919 /* xdg_type = 0 => config
1920 xdg_type = 1 => data
1921 xdg_type = 2 => cache*/
1923 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1924 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1927 gchar* temporary_string;
1931 if (! file_exists (temporary_file) && xdg_type != 2) {
1932 buf = get_xdg_var (XDG[3 + xdg_type]);
1933 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1936 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1937 g_free (temporary_file);
1938 temporary_file = g_strconcat (temporary_string, filename, NULL);
1942 //g_free (temporary_string); - segfaults.
1944 if (file_exists (temporary_file)) {
1945 return temporary_file;
1952 State *s = &uzbl.state;
1953 Network *n = &uzbl.net;
1955 for (i = 0; default_config[i].command != NULL; i++) {
1956 parse_cmd_line(default_config[i].command);
1959 if (!s->config_file) {
1960 s->config_file = find_xdg_file (0, "/uzbl/config");
1963 if (s->config_file) {
1964 GArray* lines = read_file_by_line (s->config_file);
1968 while ((line = g_array_index(lines, gchar*, i))) {
1969 parse_cmd_line (line);
1973 g_array_free (lines, TRUE);
1975 if (uzbl.state.verbose)
1976 printf ("No configuration file loaded.\n");
1979 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1982 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1985 if (!uzbl.behave.cookie_handler) return;
1987 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1988 GString *s = g_string_new ("");
1989 SoupURI * soup_uri = soup_message_get_uri(msg);
1990 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
1991 run_handler(uzbl.behave.cookie_handler, s->str);
1993 if(uzbl.comm.sync_stdout)
1994 soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
1995 //printf("stdout: %s\n", uzbl.comm.sync_stdout); // debugging
1996 if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1998 g_string_free(s, TRUE);
2002 save_cookies (SoupMessage *msg, gpointer user_data){
2006 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2007 cookie = soup_cookie_to_set_cookie_header(ck->data);
2008 SoupURI * soup_uri = soup_message_get_uri(msg);
2009 GString *s = g_string_new ("");
2010 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2011 run_handler(uzbl.behave.cookie_handler, s->str);
2013 g_string_free(s, TRUE);
2018 /* --- WEBINSPECTOR --- */
2020 hide_window_cb(GtkWidget *widget, gpointer data) {
2023 gtk_widget_hide(widget);
2026 static WebKitWebView*
2027 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2030 (void) web_inspector;
2031 GtkWidget* scrolled_window;
2032 GtkWidget* new_web_view;
2035 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2036 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2037 G_CALLBACK(hide_window_cb), NULL);
2039 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2040 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2041 gtk_widget_show(g->inspector_window);
2043 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2044 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2045 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2046 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2047 gtk_widget_show(scrolled_window);
2049 new_web_view = webkit_web_view_new();
2050 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2052 return WEBKIT_WEB_VIEW(new_web_view);
2056 inspector_show_window_cb (WebKitWebInspector* inspector){
2058 gtk_widget_show(uzbl.gui.inspector_window);
2062 /* TODO: Add variables and code to make use of these functions */
2064 inspector_close_window_cb (WebKitWebInspector* inspector){
2070 inspector_attach_window_cb (WebKitWebInspector* inspector){
2076 inspector_dettach_window_cb (WebKitWebInspector* inspector){
2082 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2088 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2094 set_up_inspector() {
2096 WebKitWebSettings *settings = webkit_web_settings_new();
2097 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2098 webkit_web_view_set_settings(WEBKIT_WEB_VIEW(uzbl.gui.web_view), settings);
2101 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2102 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2103 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2104 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2105 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2106 g_signal_connect (G_OBJECT (g->inspector), "dettach-window", G_CALLBACK (inspector_dettach_window_cb), NULL);
2107 g_signal_connect (G_OBJECT (g->inspector), "destroy", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2109 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2116 main (int argc, char* argv[]) {
2117 gtk_init (&argc, &argv);
2118 if (!g_thread_supported ())
2119 g_thread_init (NULL);
2120 uzbl.state.executable_path = g_strdup(argv[0]);
2121 uzbl.state.selected_url = NULL;
2122 uzbl.state.searchtx = NULL;
2124 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2125 g_option_context_add_main_entries (context, entries, NULL);
2126 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2127 g_option_context_parse (context, &argc, &argv, NULL);
2128 g_option_context_free(context);
2129 /* initialize hash table */
2130 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2132 uzbl.net.soup_session = webkit_get_default_session();
2133 uzbl.state.keycmd = g_string_new("");
2135 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2136 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2137 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2138 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2140 if(uname(&uzbl.state.unameinfo) == -1)
2141 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2143 uzbl.gui.sbar.progress_s = g_strdup("=");
2144 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2145 uzbl.gui.sbar.progress_w = 10;
2150 make_var_to_name_hash();
2152 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2154 uzbl.gui.scrolled_win = create_browser();
2157 /* initial packing */
2158 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2159 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2161 uzbl.gui.main_window = create_window ();
2162 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2165 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2166 gtk_widget_show_all (uzbl.gui.main_window);
2167 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2169 if (uzbl.state.verbose) {
2170 printf("Uzbl start location: %s\n", argv[0]);
2171 printf("window_id %i\n",(int) uzbl.xwin);
2172 printf("pid %i\n", getpid ());
2173 printf("name: %s\n", uzbl.state.instance_name);
2176 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2177 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2178 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2179 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2180 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2184 if (!uzbl.behave.show_status)
2185 gtk_widget_hide(uzbl.gui.mainbar);
2194 if(uzbl.state.uri) {
2195 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
2196 g_array_append_val(a, uzbl.state.uri);
2197 load_uri (uzbl.gui.web_view, a);
2198 g_array_free (a, TRUE);
2204 return EXIT_SUCCESS;
2207 /* vi: set et ts=4: */