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_STRING};
85 } var_name_to_ptr[] = {
86 /* variable name pointer to variable in code variable type callback function */
87 /* ------------------------------------------------------------------------------------------------------------------- */
88 { "uri", {.ptr = (void *)&uzbl.state.uri, .type = TYPE_STRING, .func = cmd_load_uri}},
89 { "status_message", {.ptr = (void *)&uzbl.gui.sbar.msg, .type = TYPE_STRING, .func = update_title}},
90 { "show_status", {.ptr = (void *)&uzbl.behave.show_status, .type = TYPE_INT, .func = cmd_set_status}},
91 { "status_top", {.ptr = (void *)&uzbl.behave.status_top, .type = TYPE_INT, .func = move_statusbar}},
92 { "status_format", {.ptr = (void *)&uzbl.behave.status_format, .type = TYPE_STRING, .func = update_title}},
93 { "status_pbar_done", {.ptr = (void *)&uzbl.gui.sbar.progress_s, .type = TYPE_STRING, .func = update_title}},
94 { "status_pbar_pending",{.ptr = (void *)&uzbl.gui.sbar.progress_u, .type = TYPE_STRING, .func = update_title}},
95 { "status_pbar_width", {.ptr = (void *)&uzbl.gui.sbar.progress_w, .type = TYPE_INT, .func = update_title}},
96 { "status_background", {.ptr = (void *)&uzbl.behave.status_background, .type = TYPE_STRING, .func = update_title}},
97 { "title_format_long", {.ptr = (void *)&uzbl.behave.title_format_long, .type = TYPE_STRING, .func = update_title}},
98 { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short, .type = TYPE_STRING, .func = update_title}},
99 { "insert_mode", {.ptr = (void *)&uzbl.behave.insert_mode, .type = TYPE_INT, .func = NULL}},
100 { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode, .type = TYPE_INT, .func = cmd_always_insert_mode}},
101 { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode, .type = TYPE_INT, .func = NULL}},
102 { "modkey", {.ptr = (void *)&uzbl.behave.modkey, .type = TYPE_STRING, .func = cmd_modkey}},
103 { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler, .type = TYPE_STRING, .func = NULL}},
104 { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler, .type = TYPE_STRING, .func = NULL}},
105 { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler, .type = TYPE_STRING, .func = NULL}},
106 { "history_handler", {.ptr = (void *)&uzbl.behave.history_handler, .type = TYPE_STRING, .func = NULL}},
107 { "download_handler", {.ptr = (void *)&uzbl.behave.download_handler, .type = TYPE_STRING, .func = NULL}},
108 { "cookie_handler", {.ptr = (void *)&uzbl.behave.cookie_handler, .type = TYPE_STRING, .func = NULL}},
109 { "fifo_dir", {.ptr = (void *)&uzbl.behave.fifo_dir, .type = TYPE_STRING, .func = cmd_fifo_dir}},
110 { "socket_dir", {.ptr = (void *)&uzbl.behave.socket_dir, .type = TYPE_STRING, .func = cmd_socket_dir}},
111 { "http_debug", {.ptr = (void *)&uzbl.behave.http_debug, .type = TYPE_INT, .func = cmd_http_debug}},
112 { "default_font_size", {.ptr = (void *)&uzbl.behave.default_font_size, .type = TYPE_INT, .func = cmd_default_font_size}},
113 { "default_monospace_size", {.ptr = (void *)&uzbl.behave.default_monospace_size, .type = TYPE_INT, .func = cmd_default_font_size}},
114 { "minimum_font_size", {.ptr = (void *)&uzbl.behave.minimum_font_size, .type = TYPE_INT, .func = cmd_minimum_font_size}},
115 { "disable_plugins", {.ptr = (void *)&uzbl.behave.disable_plugins, .type = TYPE_INT, .func = cmd_disable_plugins}},
116 { "shell_cmd", {.ptr = (void *)&uzbl.behave.shell_cmd, .type = TYPE_STRING, .func = NULL}},
117 { "proxy_url", {.ptr = (void *)&uzbl.net.proxy_url, .type = TYPE_STRING, .func = set_proxy_url}},
118 { "max_conns", {.ptr = (void *)&uzbl.net.max_conns, .type = TYPE_INT, .func = cmd_max_conns}},
119 { "max_conns_host", {.ptr = (void *)&uzbl.net.max_conns_host, .type = TYPE_INT, .func = cmd_max_conns_host}},
120 { "useragent", {.ptr = (void *)&uzbl.net.useragent, .type = TYPE_STRING, .func = cmd_useragent}},
121 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
122 }, *n2v_p = var_name_to_ptr;
128 { "SHIFT", GDK_SHIFT_MASK }, // shift
129 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
130 { "CONTROL", GDK_CONTROL_MASK }, // control
131 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
132 { "MOD2", GDK_MOD2_MASK }, // 5th mod
133 { "MOD3", GDK_MOD3_MASK }, // 6th mod
134 { "MOD4", GDK_MOD4_MASK }, // 7th mod
135 { "MOD5", GDK_MOD5_MASK }, // 8th mod
136 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
137 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
138 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
139 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
140 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
141 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
142 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
143 { "META", GDK_META_MASK }, // meta (since 2.10)
148 /* construct a hash from the var_name_to_ptr array for quick access */
150 make_var_to_name_hash() {
151 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
153 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
159 /* --- UTILITY FUNCTIONS --- */
165 snprintf(tmp, sizeof(tmp), "%i", val);
166 return g_strdup(tmp);
170 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
173 str_replace (const char* search, const char* replace, const char* string) {
177 buf = g_strsplit (string, search, -1);
178 ret = g_strjoinv (replace, buf);
179 g_strfreev(buf); // somebody said this segfaults
185 read_file_by_line (gchar *path) {
186 GIOChannel *chan = NULL;
187 gchar *readbuf = NULL;
189 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
192 chan = g_io_channel_new_file(path, "r", NULL);
195 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
196 const gchar* val = g_strdup (readbuf);
197 g_array_append_val (lines, val);
202 g_io_channel_unref (chan);
204 fprintf(stderr, "File '%s' not be read.\n", path);
211 gchar* parseenv (char* string) {
212 extern char** environ;
213 gchar* tmpstr = NULL;
217 while (environ[i] != NULL) {
218 gchar** env = g_strsplit (environ[i], "=", 2);
219 gchar* envname = g_strconcat ("$", env[0], NULL);
221 if (g_strrstr (string, envname) != NULL) {
222 tmpstr = g_strdup(string);
224 string = str_replace(envname, env[1], tmpstr);
229 g_strfreev (env); // somebody said this breaks uzbl
237 setup_signal(int signr, sigfunc *shandler) {
238 struct sigaction nh, oh;
240 nh.sa_handler = shandler;
241 sigemptyset(&nh.sa_mask);
244 if(sigaction(signr, &nh, &oh) < 0)
252 if (uzbl.behave.fifo_dir)
253 unlink (uzbl.comm.fifo_path);
254 if (uzbl.behave.socket_dir)
255 unlink (uzbl.comm.socket_path);
257 g_free(uzbl.state.executable_path);
258 g_string_free(uzbl.state.keycmd, TRUE);
259 g_hash_table_destroy(uzbl.bindings);
260 g_hash_table_destroy(uzbl.behave.commands);
264 /* --- SIGNAL HANDLER --- */
267 catch_sigterm(int s) {
273 catch_sigint(int s) {
279 /* --- CALLBACKS --- */
282 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
285 (void) navigation_action;
286 (void) policy_decision;
288 const gchar* uri = webkit_network_request_get_uri (request);
289 if (uzbl.state.verbose)
290 printf("New window requested -> %s \n", uri);
291 new_window_load_uri(uri);
296 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
300 if (uzbl.state.selected_url != NULL) {
301 if (uzbl.state.verbose)
302 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
303 new_window_load_uri(uzbl.state.selected_url);
305 if (uzbl.state.verbose)
306 printf("New web view -> %s\n","Nothing to open, exiting");
312 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
315 if (uzbl.behave.download_handler) {
316 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
317 if (uzbl.state.verbose)
318 printf("Download -> %s\n",uri);
319 /* if urls not escaped, we may have to escape and quote uri before this call */
320 run_handler(uzbl.behave.download_handler, uri);
325 /* scroll a bar in a given direction */
327 scroll (GtkAdjustment* bar, GArray *argv) {
331 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
332 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
333 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
336 static void scroll_begin(WebKitWebView* page, GArray *argv) {
337 (void) page; (void) argv;
338 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
341 static void scroll_end(WebKitWebView* page, GArray *argv) {
342 (void) page; (void) argv;
343 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
344 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
347 static void scroll_vert(WebKitWebView* page, GArray *argv) {
349 scroll(uzbl.gui.bar_v, argv);
352 static void scroll_horz(WebKitWebView* page, GArray *argv) {
354 scroll(uzbl.gui.bar_h, argv);
359 if (!uzbl.behave.show_status) {
360 gtk_widget_hide(uzbl.gui.mainbar);
362 gtk_widget_show(uzbl.gui.mainbar);
368 toggle_status_cb (WebKitWebView* page, GArray *argv) {
372 if (uzbl.behave.show_status) {
373 gtk_widget_hide(uzbl.gui.mainbar);
375 gtk_widget_show(uzbl.gui.mainbar);
377 uzbl.behave.show_status = !uzbl.behave.show_status;
382 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
386 //Set selected_url state variable
387 g_free(uzbl.state.selected_url);
388 uzbl.state.selected_url = NULL;
390 uzbl.state.selected_url = g_strdup(link);
396 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
400 if (uzbl.gui.main_title)
401 g_free (uzbl.gui.main_title);
402 uzbl.gui.main_title = g_strdup (title);
407 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
410 uzbl.gui.sbar.load_progress = progress;
415 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
419 if (uzbl.behave.load_finish_handler)
420 run_handler(uzbl.behave.load_finish_handler, "");
424 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
428 if (uzbl.behave.load_start_handler)
429 run_handler(uzbl.behave.load_start_handler, "");
433 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
436 g_free (uzbl.state.uri);
437 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
438 uzbl.state.uri = g_string_free (newuri, FALSE);
439 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
440 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
443 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
444 if (uzbl.behave.load_commit_handler)
445 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
449 destroy_cb (GtkWidget* widget, gpointer data) {
457 if (uzbl.behave.history_handler) {
459 struct tm * timeinfo;
462 timeinfo = localtime ( &rawtime );
463 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
464 run_handler(uzbl.behave.history_handler, date);
469 /* VIEW funcs (little webkit wrappers) */
470 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
472 VIEWFUNC(reload_bypass_cache)
473 VIEWFUNC(stop_loading)
480 /* -- command to callback/function map for things we cannot attach to any signals */
482 static struct {char *name; Command command[2];} cmdlist[] =
483 { /* key function no_split */
484 { "back", {view_go_back, 0} },
485 { "forward", {view_go_forward, 0} },
486 { "scroll_vert", {scroll_vert, 0} },
487 { "scroll_horz", {scroll_horz, 0} },
488 { "scroll_begin", {scroll_begin, 0} },
489 { "scroll_end", {scroll_end, 0} },
490 { "reload", {view_reload, 0}, },
491 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
492 { "stop", {view_stop_loading, 0}, },
493 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
494 { "zoom_out", {view_zoom_out, 0}, },
495 { "uri", {load_uri, NOSPLIT} },
496 { "js", {run_js, NOSPLIT} },
497 { "script", {run_external_js, 0} },
498 { "toggle_status", {toggle_status_cb, 0} },
499 { "spawn", {spawn, 0} },
500 { "sh", {spawn_sh, 0} },
501 { "exit", {close_uzbl, 0} },
502 { "search", {search_forward_text, NOSPLIT} },
503 { "search_reverse", {search_reverse_text, NOSPLIT} },
504 { "toggle_insert_mode", {toggle_insert_mode, 0} },
505 { "runcmd", {runcmd, NOSPLIT} }
512 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
514 for (i = 0; i < LENGTH(cmdlist); i++)
515 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
518 /* -- CORE FUNCTIONS -- */
521 free_action(gpointer act) {
522 Action *action = (Action*)act;
523 g_free(action->name);
525 g_free(action->param);
530 new_action(const gchar *name, const gchar *param) {
531 Action *action = g_new(Action, 1);
533 action->name = g_strdup(name);
535 action->param = g_strdup(param);
537 action->param = NULL;
543 file_exists (const char * filename) {
544 return (access(filename, F_OK) == 0);
548 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
551 if (argv_idx(argv, 0)) {
552 if (strcmp (argv_idx(argv, 0), "0") == 0) {
553 uzbl.behave.insert_mode = FALSE;
555 uzbl.behave.insert_mode = TRUE;
558 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
565 load_uri (WebKitWebView *web_view, GArray *argv) {
566 if (argv_idx(argv, 0)) {
567 GString* newuri = g_string_new (argv_idx(argv, 0));
568 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
569 g_string_prepend (newuri, "http://");
570 /* if we do handle cookies, ask our handler for them */
571 webkit_web_view_load_uri (web_view, newuri->str);
572 g_string_free (newuri, TRUE);
577 run_js (WebKitWebView * web_view, GArray *argv) {
578 if (argv_idx(argv, 0))
579 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
583 run_external_js (WebKitWebView * web_view, GArray *argv) {
584 if (argv_idx(argv, 0)) {
585 GArray* lines = read_file_by_line (argv_idx (argv, 0));
590 while ((line = g_array_index(lines, gchar*, i))) {
592 js = g_strdup (line);
594 gchar* newjs = g_strconcat (js, line, NULL);
601 if (uzbl.state.verbose)
602 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
604 if (argv_idx (argv, 1)) {
605 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
609 webkit_web_view_execute_script (web_view, js);
611 g_array_free (lines, TRUE);
616 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
617 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
618 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
619 webkit_web_view_unmark_text_matches (page);
620 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
621 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
625 if (uzbl.state.searchtx) {
626 if (uzbl.state.verbose)
627 printf ("Searching: %s\n", uzbl.state.searchtx);
628 webkit_web_view_set_highlight_text_matches (page, TRUE);
629 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
634 search_forward_text (WebKitWebView *page, GArray *argv) {
635 search_text(page, argv, TRUE);
639 search_reverse_text (WebKitWebView *page, GArray *argv) {
640 search_text(page, argv, FALSE);
644 new_window_load_uri (const gchar * uri) {
645 GString* to_execute = g_string_new ("");
646 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
648 for (i = 0; entries[i].long_name != NULL; i++) {
649 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
650 gchar** str = (gchar**)entries[i].arg_data;
652 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
656 if (uzbl.state.verbose)
657 printf("\n%s\n", to_execute->str);
658 g_spawn_command_line_async (to_execute->str, NULL);
659 g_string_free (to_execute, TRUE);
663 close_uzbl (WebKitWebView *page, GArray *argv) {
669 /* --Statusbar functions-- */
671 build_progressbar_ascii(int percent) {
672 int width=uzbl.gui.sbar.progress_w;
675 GString *bar = g_string_new("");
677 l = (double)percent*((double)width/100.);
678 l = (int)(l+.5)>=(int)l ? l+.5 : l;
680 for(i=0; i<(int)l; i++)
681 g_string_append(bar, uzbl.gui.sbar.progress_s);
684 g_string_append(bar, uzbl.gui.sbar.progress_u);
686 return g_string_free(bar, FALSE);
691 const GScannerConfig scan_config = {
694 ) /* cset_skip_characters */,
699 ) /* cset_identifier_first */,
706 ) /* cset_identifier_nth */,
707 ( "" ) /* cpair_comment_single */,
709 TRUE /* case_sensitive */,
711 FALSE /* skip_comment_multi */,
712 FALSE /* skip_comment_single */,
713 FALSE /* scan_comment_multi */,
714 TRUE /* scan_identifier */,
715 TRUE /* scan_identifier_1char */,
716 FALSE /* scan_identifier_NULL */,
717 TRUE /* scan_symbols */,
718 FALSE /* scan_binary */,
719 FALSE /* scan_octal */,
720 FALSE /* scan_float */,
721 FALSE /* scan_hex */,
722 FALSE /* scan_hex_dollar */,
723 FALSE /* scan_string_sq */,
724 FALSE /* scan_string_dq */,
725 TRUE /* numbers_2_int */,
726 FALSE /* int_2_float */,
727 FALSE /* identifier_2_string */,
728 FALSE /* char_2_token */,
729 FALSE /* symbol_2_token */,
730 TRUE /* scope_0_fallback */,
735 uzbl.scan = g_scanner_new(&scan_config);
736 while(symp->symbol_name) {
737 g_scanner_scope_add_symbol(uzbl.scan, 0,
739 GINT_TO_POINTER(symp->symbol_token));
745 expand_template(const char *template) {
746 if(!template) return NULL;
748 GTokenType token = G_TOKEN_NONE;
749 GString *ret = g_string_new("");
753 g_scanner_input_text(uzbl.scan, template, strlen(template));
754 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
755 token = g_scanner_get_next_token(uzbl.scan);
757 if(token == G_TOKEN_SYMBOL) {
758 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
761 buf = uzbl.state.uri?
762 g_markup_printf_escaped("%s", uzbl.state.uri) :
764 g_string_append(ret, buf);
768 buf = itos(uzbl.gui.sbar.load_progress);
769 g_string_append(ret, buf);
772 case SYM_LOADPRGSBAR:
773 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
774 g_string_append(ret, buf);
778 buf = uzbl.gui.main_title?
779 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
781 g_string_append(ret, buf);
784 case SYM_SELECTED_URI:
785 buf = uzbl.state.selected_url?
786 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
788 g_string_append(ret, buf);
792 buf = itos(uzbl.xwin);
794 uzbl.state.instance_name?uzbl.state.instance_name:buf);
798 buf = uzbl.state.keycmd->str?
799 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
801 g_string_append(ret, buf);
806 uzbl.behave.insert_mode?"[I]":"[C]");
810 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
814 buf = itos(WEBKIT_MAJOR_VERSION);
815 g_string_append(ret, buf);
819 buf = itos(WEBKIT_MINOR_VERSION);
820 g_string_append(ret, buf);
824 buf = itos(WEBKIT_MICRO_VERSION);
825 g_string_append(ret, buf);
829 g_string_append(ret, uzbl.state.unameinfo.sysname);
832 g_string_append(ret, uzbl.state.unameinfo.nodename);
835 g_string_append(ret, uzbl.state.unameinfo.release);
838 g_string_append(ret, uzbl.state.unameinfo.version);
841 g_string_append(ret, uzbl.state.unameinfo.machine);
844 g_string_append(ret, ARCH);
848 g_string_append(ret, uzbl.state.unameinfo.domainname);
852 g_string_append(ret, COMMIT);
858 else if(token == G_TOKEN_INT) {
859 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
860 g_string_append(ret, buf);
863 else if(token == G_TOKEN_IDENTIFIER) {
864 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
866 else if(token == G_TOKEN_CHAR) {
867 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
871 return g_string_free(ret, FALSE);
873 /* --End Statusbar functions-- */
876 sharg_append(GArray *a, const gchar *str) {
877 const gchar *s = (str ? str : "");
878 g_array_append_val(a, s);
881 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
883 run_command (const gchar *command, const guint npre, const gchar **args,
884 const gboolean sync, char **stdout) {
885 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
888 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
889 gchar *pid = itos(getpid());
890 gchar *xwin = itos(uzbl.xwin);
892 sharg_append(a, command);
893 for (i = 0; i < npre; i++) /* add n args before the default vars */
894 sharg_append(a, args[i]);
895 sharg_append(a, uzbl.state.config_file);
896 sharg_append(a, pid);
897 sharg_append(a, xwin);
898 sharg_append(a, uzbl.comm.fifo_path);
899 sharg_append(a, uzbl.comm.socket_path);
900 sharg_append(a, uzbl.state.uri);
901 sharg_append(a, uzbl.gui.main_title);
903 for (i = npre; i < g_strv_length((gchar**)args); i++)
904 sharg_append(a, args[i]);
907 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
908 NULL, NULL, stdout, NULL, NULL, &err);
909 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
910 NULL, NULL, NULL, &err);
912 if (uzbl.state.verbose) {
913 GString *s = g_string_new("spawned:");
914 for (i = 0; i < (a->len); i++) {
915 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
916 g_string_append_printf(s, " %s", qarg);
919 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
920 printf("%s\n", s->str);
921 g_string_free(s, TRUE);
924 g_printerr("error on run_command: %s\n", err->message);
929 g_array_free (a, TRUE);
934 split_quoted(const gchar* src, const gboolean unquote) {
935 /* split on unquoted space, return array of strings;
936 remove a layer of quotes and backslashes if unquote */
937 if (!src) return NULL;
941 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
942 GString *s = g_string_new ("");
946 for (p = src; *p != '\0'; p++) {
947 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
948 else if (*p == '\\') { g_string_append_c(s, *p++);
949 g_string_append_c(s, *p); }
950 else if ((*p == '"') && unquote && !sq) dq = !dq;
951 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
953 else if ((*p == '\'') && unquote && !dq) sq = !sq;
954 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
956 else if ((*p == ' ') && !dq && !sq) {
957 dup = g_strdup(s->str);
958 g_array_append_val(a, dup);
959 g_string_truncate(s, 0);
960 } else g_string_append_c(s, *p);
962 dup = g_strdup(s->str);
963 g_array_append_val(a, dup);
964 ret = (gchar**)a->data;
965 g_array_free (a, FALSE);
966 g_string_free (s, TRUE);
971 spawn(WebKitWebView *web_view, GArray *argv) {
973 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
974 if (argv_idx(argv, 0))
975 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
979 spawn_sh(WebKitWebView *web_view, GArray *argv) {
981 if (!uzbl.behave.shell_cmd) {
982 g_printerr ("spawn_sh: shell_cmd is not set!\n");
987 gchar *spacer = g_strdup("");
988 g_array_insert_val(argv, 1, spacer);
989 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
991 for (i = 1; i < g_strv_length(cmd); i++)
992 g_array_prepend_val(argv, cmd[i]);
994 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1000 parse_command(const char *cmd, const char *param) {
1003 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1006 gchar **par = split_quoted(param, TRUE);
1007 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1009 if (c[1] == NOSPLIT) { /* don't split */
1010 sharg_append(a, param);
1012 for (i = 0; i < g_strv_length(par); i++)
1013 sharg_append(a, par[i]);
1015 c[0](uzbl.gui.web_view, a);
1017 g_array_free (a, TRUE);
1020 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1023 /* command parser */
1026 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1027 G_REGEX_OPTIMIZE, 0, NULL);
1028 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1029 G_REGEX_OPTIMIZE, 0, NULL);
1030 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1031 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1032 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1033 G_REGEX_OPTIMIZE, 0, NULL);
1034 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1035 G_REGEX_OPTIMIZE, 0, NULL);
1039 get_var_value(gchar *name) {
1042 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1043 if(c->type == TYPE_STRING)
1044 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1045 else if(c->type == TYPE_INT)
1046 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1055 if(*uzbl.net.proxy_url == ' '
1056 || uzbl.net.proxy_url == NULL) {
1057 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1058 (GType) SOUP_SESSION_PROXY_URI);
1061 suri = soup_uri_new(uzbl.net.proxy_url);
1062 g_object_set(G_OBJECT(uzbl.net.soup_session),
1063 SOUP_SESSION_PROXY_URI,
1065 soup_uri_free(suri);
1072 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1073 g_array_append_val (a, uzbl.state.uri);
1074 load_uri(uzbl.gui.web_view, a);
1075 g_array_free (a, TRUE);
1079 cmd_always_insert_mode() {
1080 uzbl.behave.insert_mode =
1081 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1087 g_object_set(G_OBJECT(uzbl.net.soup_session),
1088 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1092 cmd_max_conns_host() {
1093 g_object_set(G_OBJECT(uzbl.net.soup_session),
1094 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1099 soup_session_remove_feature
1100 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1101 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1102 /*g_free(uzbl.net.soup_logger);*/
1104 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1105 soup_session_add_feature(uzbl.net.soup_session,
1106 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1110 cmd_default_font_size() {
1111 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1112 if (uzbl.behave.default_font_size > 0) {
1113 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1116 if (uzbl.behave.default_monospace_size > 0) {
1117 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1118 uzbl.behave.default_monospace_size, NULL);
1120 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1121 uzbl.behave.default_font_size, NULL);
1126 cmd_disable_plugins() {
1127 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1128 g_object_set (G_OBJECT(ws), "enable-plugins", !uzbl.behave.disable_plugins, NULL);
1132 cmd_minimum_font_size() {
1133 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1134 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1139 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1144 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1152 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1153 uzbl.behave.modmask = 0;
1155 if(uzbl.behave.modkey)
1156 g_free(uzbl.behave.modkey);
1157 uzbl.behave.modkey = buf;
1159 for (i = 0; modkeys[i].key != NULL; i++) {
1160 if (g_strrstr(buf, modkeys[i].key))
1161 uzbl.behave.modmask |= modkeys[i].mask;
1167 if (*uzbl.net.useragent == ' ') {
1168 g_free (uzbl.net.useragent);
1169 uzbl.net.useragent = NULL;
1171 gchar *ua = expand_template(uzbl.net.useragent);
1173 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1174 g_free(uzbl.net.useragent);
1175 uzbl.net.useragent = ua;
1181 gtk_widget_ref(uzbl.gui.scrolled_win);
1182 gtk_widget_ref(uzbl.gui.mainbar);
1183 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1184 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1186 if(uzbl.behave.status_top) {
1187 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1188 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1191 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1192 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1194 gtk_widget_unref(uzbl.gui.scrolled_win);
1195 gtk_widget_unref(uzbl.gui.mainbar);
1196 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1201 set_var_value(gchar *name, gchar *val) {
1202 uzbl_cmdprop *c = NULL;
1205 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1206 /* check for the variable type */
1207 if (c->type == TYPE_STRING) {
1209 *c->ptr = g_strdup(val);
1210 } else if(c->type == TYPE_INT) {
1211 int *ip = GPOINTER_TO_INT(c->ptr);
1212 *ip = (int)strtoul(val, &endp, 10);
1215 /* invoke a command specific function */
1216 if(c->func) c->func();
1222 runcmd(WebKitWebView* page, GArray *argv) {
1224 parse_cmd_line(argv_idx(argv, 0));
1228 parse_cmd_line(const char *ctl_line) {
1229 gchar **tokens = NULL;
1232 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1233 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1234 if(tokens[0][0] == 0) {
1235 gchar* value = parseenv(g_strdup(tokens[2]));
1236 set_var_value(tokens[1], value);
1240 printf("Error in command: %s\n", tokens[0]);
1243 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1244 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1245 if(tokens[0][0] == 0) {
1246 get_var_value(tokens[1]);
1249 printf("Error in command: %s\n", tokens[0]);
1252 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1253 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1254 if(tokens[0][0] == 0) {
1255 gchar* value = parseenv(g_strdup(tokens[2]));
1256 add_binding(tokens[1], value);
1260 printf("Error in command: %s\n", tokens[0]);
1263 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1264 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1265 if(tokens[0][0] == 0) {
1266 parse_command(tokens[1], tokens[2]);
1269 printf("Error in command: %s\n", tokens[0]);
1271 /* KEYCMD command */
1272 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1273 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1274 if(tokens[0][0] == 0) {
1275 /* should incremental commands want each individual "keystroke"
1276 sent in a loop or the whole string in one go like now? */
1277 g_string_assign(uzbl.state.keycmd, tokens[1]);
1279 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1285 else if( (ctl_line[0] == '#')
1286 || (ctl_line[0] == ' ')
1287 || (ctl_line[0] == '\n'))
1288 ; /* ignore these lines */
1290 printf("Command not understood (%s)\n", ctl_line);
1299 build_stream_name(int type, const gchar* dir) {
1301 State *s = &uzbl.state;
1304 xwin_str = itos((int)uzbl.xwin);
1306 str = g_strdup_printf
1307 ("%s/uzbl_fifo_%s", dir,
1308 s->instance_name ? s->instance_name : xwin_str);
1309 } else if (type == SOCKET) {
1310 str = g_strdup_printf
1311 ("%s/uzbl_socket_%s", dir,
1312 s->instance_name ? s->instance_name : xwin_str );
1319 control_fifo(GIOChannel *gio, GIOCondition condition) {
1320 if (uzbl.state.verbose)
1321 printf("triggered\n");
1326 if (condition & G_IO_HUP)
1327 g_error ("Fifo: Read end of pipe died!\n");
1330 g_error ("Fifo: GIOChannel broke\n");
1332 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1333 if (ret == G_IO_STATUS_ERROR) {
1334 g_error ("Fifo: Error reading: %s\n", err->message);
1338 parse_cmd_line(ctl_line);
1345 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1346 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1347 if (unlink(uzbl.comm.fifo_path) == -1)
1348 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1349 g_free(uzbl.comm.fifo_path);
1350 uzbl.comm.fifo_path = NULL;
1353 if (*dir == ' ') { /* space unsets the variable */
1358 GIOChannel *chan = NULL;
1359 GError *error = NULL;
1360 gchar *path = build_stream_name(FIFO, dir);
1362 if (!file_exists(path)) {
1363 if (mkfifo (path, 0666) == 0) {
1364 // 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.
1365 chan = g_io_channel_new_file(path, "r+", &error);
1367 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1368 if (uzbl.state.verbose)
1369 printf ("init_fifo: created successfully as %s\n", path);
1370 uzbl.comm.fifo_path = path;
1372 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1373 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1374 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1375 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1377 /* if we got this far, there was an error; cleanup */
1378 if (error) g_error_free (error);
1385 control_stdin(GIOChannel *gio, GIOCondition condition) {
1387 gchar *ctl_line = NULL;
1390 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1391 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1394 parse_cmd_line(ctl_line);
1402 GIOChannel *chan = NULL;
1403 GError *error = NULL;
1405 chan = g_io_channel_unix_new(fileno(stdin));
1407 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1408 g_error ("Stdin: could not add watch\n");
1410 if (uzbl.state.verbose)
1411 printf ("Stdin: watch added successfully\n");
1414 g_error ("Stdin: Error while opening: %s\n", error->message);
1416 if (error) g_error_free (error);
1420 control_socket(GIOChannel *chan) {
1421 struct sockaddr_un remote;
1422 char buffer[512], *ctl_line;
1424 int sock, clientsock, n, done;
1427 sock = g_io_channel_unix_get_fd(chan);
1429 memset (buffer, 0, sizeof (buffer));
1431 t = sizeof (remote);
1432 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1436 memset (temp, 0, sizeof (temp));
1437 n = recv (clientsock, temp, 128, 0);
1439 buffer[strlen (buffer)] = '\0';
1443 strcat (buffer, temp);
1446 if (strcmp (buffer, "\n") < 0) {
1447 buffer[strlen (buffer) - 1] = '\0';
1449 buffer[strlen (buffer)] = '\0';
1452 ctl_line = g_strdup(buffer);
1453 parse_cmd_line (ctl_line);
1456 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1457 GError *error = NULL;
1460 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1461 if (ret == G_IO_STATUS_ERROR)
1462 g_error ("Error reading: %s\n", error->message);
1464 printf("Got line %s (%u bytes) \n",ctl_line, len);
1466 parse_line(ctl_line);
1474 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1475 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1476 if (unlink(uzbl.comm.socket_path) == -1)
1477 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1478 g_free(uzbl.comm.socket_path);
1479 uzbl.comm.socket_path = NULL;
1487 GIOChannel *chan = NULL;
1489 struct sockaddr_un local;
1490 gchar *path = build_stream_name(SOCKET, dir);
1492 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1494 local.sun_family = AF_UNIX;
1495 strcpy (local.sun_path, path);
1496 unlink (local.sun_path);
1498 len = strlen (local.sun_path) + sizeof (local.sun_family);
1499 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1500 if (uzbl.state.verbose)
1501 printf ("init_socket: opened in %s\n", path);
1504 if( (chan = g_io_channel_unix_new(sock)) ) {
1505 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1506 uzbl.comm.socket_path = path;
1509 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1511 /* if we got this far, there was an error; cleanup */
1518 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1519 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1521 // this function may be called very early when the templates are not set (yet), hence the checks
1523 update_title (void) {
1524 Behaviour *b = &uzbl.behave;
1527 if (b->show_status) {
1528 if (b->title_format_short) {
1529 parsed = expand_template(b->title_format_short);
1530 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1533 if (b->status_format) {
1534 parsed = expand_template(b->status_format);
1535 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1538 if (b->status_background) {
1540 gdk_color_parse (b->status_background, &color);
1541 //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)
1542 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1545 if (b->title_format_long) {
1546 parsed = expand_template(b->title_format_long);
1547 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1554 key_press_cb (GtkWidget* window, GdkEventKey* event)
1556 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1560 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1561 || 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)
1564 /* turn off insert mode (if always_insert_mode is not used) */
1565 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1566 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1571 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1574 if (event->keyval == GDK_Escape) {
1575 g_string_truncate(uzbl.state.keycmd, 0);
1580 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1581 if (event->keyval == GDK_Insert) {
1583 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1584 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1586 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1589 g_string_append (uzbl.state.keycmd, str);
1596 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1597 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1601 gboolean key_ret = FALSE;
1602 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1604 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1606 run_keycmd(key_ret);
1608 if (key_ret) return (!uzbl.behave.insert_mode);
1613 run_keycmd(const gboolean key_ret) {
1614 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1616 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1617 g_string_truncate(uzbl.state.keycmd, 0);
1618 parse_command(action->name, action->param);
1622 /* try if it's an incremental keycmd or one that takes args, and run it */
1623 GString* short_keys = g_string_new ("");
1624 GString* short_keys_inc = g_string_new ("");
1626 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1627 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1628 g_string_assign(short_keys_inc, short_keys->str);
1629 g_string_append_c(short_keys, '_');
1630 g_string_append_c(short_keys_inc, '*');
1632 gboolean exec_now = FALSE;
1633 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1634 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1635 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1636 if (key_ret) { /* just quit the incremental command on return */
1637 g_string_truncate(uzbl.state.keycmd, 0);
1639 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1643 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1644 GString* actionname = g_string_new ("");
1645 GString* actionparam = g_string_new ("");
1646 g_string_erase (parampart, 0, i+1);
1648 g_string_printf (actionname, action->name, parampart->str);
1650 g_string_printf (actionparam, action->param, parampart->str);
1651 parse_command(actionname->str, actionparam->str);
1652 g_string_free (actionname, TRUE);
1653 g_string_free (actionparam, TRUE);
1654 g_string_free (parampart, TRUE);
1656 g_string_truncate(uzbl.state.keycmd, 0);
1660 g_string_truncate(short_keys, short_keys->len - 1);
1662 g_string_free (short_keys, TRUE);
1663 g_string_free (short_keys_inc, TRUE);
1670 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1671 //main_window_ref = g_object_ref(scrolled_window);
1672 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
1674 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1675 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1677 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1678 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1679 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1680 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1681 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1682 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1683 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1684 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1685 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1686 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1688 return scrolled_window;
1695 g->mainbar = gtk_hbox_new (FALSE, 0);
1697 /* keep a reference to the bar so we can re-pack it at runtime*/
1698 //sbar_ref = g_object_ref(g->mainbar);
1700 g->mainbar_label = gtk_label_new ("");
1701 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1702 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1703 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1704 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1705 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1710 GtkWidget* create_window () {
1711 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1712 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1713 gtk_widget_set_name (window, "Uzbl browser");
1714 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1715 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1721 run_handler (const gchar *act, const gchar *args) {
1722 char **parts = g_strsplit(act, " ", 2);
1724 else if ((g_strcmp0(parts[0], "spawn") == 0)
1725 || (g_strcmp0(parts[0], "sh") == 0)) {
1727 GString *a = g_string_new ("");
1729 spawnparts = split_quoted(parts[1], FALSE);
1730 g_string_append_printf(a, "%s", spawnparts[0]);
1731 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1732 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1733 g_string_append_printf(a, " %s", spawnparts[i]);
1734 parse_command(parts[0], a->str);
1735 g_string_free (a, TRUE);
1736 g_strfreev (spawnparts);
1738 parse_command(parts[0], parts[1]);
1743 add_binding (const gchar *key, const gchar *act) {
1744 char **parts = g_strsplit(act, " ", 2);
1751 if (uzbl.state.verbose)
1752 printf ("Binding %-10s : %s\n", key, act);
1753 action = new_action(parts[0], parts[1]);
1755 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1760 get_xdg_var (XDG_Var xdg) {
1761 const gchar* actual_value = getenv (xdg.environmental);
1762 const gchar* home = getenv ("HOME");
1764 gchar* return_value = str_replace ("~", home, actual_value);
1766 if (! actual_value || strcmp (actual_value, "") == 0) {
1767 if (xdg.default_value) {
1768 return_value = str_replace ("~", home, xdg.default_value);
1770 return_value = NULL;
1773 return return_value;
1777 find_xdg_file (int xdg_type, char* filename) {
1778 /* xdg_type = 0 => config
1779 xdg_type = 1 => data
1780 xdg_type = 2 => cache*/
1782 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1783 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1786 gchar* temporary_string;
1790 if (! file_exists (temporary_file) && xdg_type != 2) {
1791 buf = get_xdg_var (XDG[3 + xdg_type]);
1792 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1795 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1796 g_free (temporary_file);
1797 temporary_file = g_strconcat (temporary_string, filename, NULL);
1801 //g_free (temporary_string); - segfaults.
1803 if (file_exists (temporary_file)) {
1804 return temporary_file;
1811 State *s = &uzbl.state;
1812 Network *n = &uzbl.net;
1814 for (i = 0; default_config[i].command != NULL; i++) {
1815 parse_cmd_line(default_config[i].command);
1818 if (!s->config_file) {
1819 s->config_file = find_xdg_file (0, "/uzbl/config");
1822 if (s->config_file) {
1823 GArray* lines = read_file_by_line (s->config_file);
1827 while ((line = g_array_index(lines, gchar*, i))) {
1828 parse_cmd_line (line);
1832 g_array_free (lines, TRUE);
1834 if (uzbl.state.verbose)
1835 printf ("No configuration file loaded.\n");
1838 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1841 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1844 if (!uzbl.behave.cookie_handler) return;
1846 gchar * stdout = NULL;
1847 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1848 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1849 gchar *action = g_strdup ("GET");
1850 SoupURI * soup_uri = soup_message_get_uri(msg);
1851 sharg_append(a, action);
1852 sharg_append(a, soup_uri->host);
1853 sharg_append(a, soup_uri->path);
1854 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1855 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1857 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1860 g_array_free(a, TRUE);
1864 save_cookies (SoupMessage *msg, gpointer user_data){
1868 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1869 cookie = soup_cookie_to_set_cookie_header(ck->data);
1870 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1871 SoupURI * soup_uri = soup_message_get_uri(msg);
1872 gchar *action = strdup("PUT");
1873 sharg_append(a, action);
1874 sharg_append(a, soup_uri->host);
1875 sharg_append(a, soup_uri->path);
1876 sharg_append(a, cookie);
1877 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1880 g_array_free(a, TRUE);
1886 main (int argc, char* argv[]) {
1887 gtk_init (&argc, &argv);
1888 if (!g_thread_supported ())
1889 g_thread_init (NULL);
1890 uzbl.state.executable_path = g_strdup(argv[0]);
1891 uzbl.state.selected_url = NULL;
1892 uzbl.state.searchtx = NULL;
1894 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1895 g_option_context_add_main_entries (context, entries, NULL);
1896 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1897 g_option_context_parse (context, &argc, &argv, NULL);
1898 g_option_context_free(context);
1899 /* initialize hash table */
1900 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1902 uzbl.net.soup_session = webkit_get_default_session();
1903 uzbl.state.keycmd = g_string_new("");
1905 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1906 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1907 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1908 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1910 if(uname(&uzbl.state.unameinfo) == -1)
1911 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1913 uzbl.gui.sbar.progress_s = g_strdup("=");
1914 uzbl.gui.sbar.progress_u = g_strdup("ยท");
1915 uzbl.gui.sbar.progress_w = 10;
1920 make_var_to_name_hash();
1922 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1924 uzbl.gui.scrolled_win = create_browser();
1927 /* initial packing */
1928 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1929 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1931 uzbl.gui.main_window = create_window ();
1932 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1935 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1936 gtk_widget_show_all (uzbl.gui.main_window);
1937 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1939 if (uzbl.state.verbose) {
1940 printf("Uzbl start location: %s\n", argv[0]);
1941 printf("window_id %i\n",(int) uzbl.xwin);
1942 printf("pid %i\n", getpid ());
1943 printf("name: %s\n", uzbl.state.instance_name);
1946 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1947 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1948 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1949 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1950 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1954 if (!uzbl.behave.show_status)
1955 gtk_widget_hide(uzbl.gui.mainbar);
1961 if(uzbl.state.uri) {
1962 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1963 g_array_append_val(a, uzbl.state.uri);
1964 load_uri (uzbl.gui.web_view, a);
1965 g_array_free (a, TRUE);
1971 return EXIT_SUCCESS;
1974 /* vi: set et ts=4: */