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); - 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 (const char* string) {
212 extern char** environ;
213 gchar* newstring = g_strdup (string);
216 while (environ[i] != NULL) {
217 gchar** env = g_strsplit (environ[i], "=", 0);
218 gchar* envname = g_strconcat ("$", env[0], NULL);
220 if (g_strrstr (newstring, envname) != NULL) {
221 newstring = str_replace(envname, env[1], newstring);
225 //g_strfreev (env); //- This still breaks uzbl, but shouldn't. The mystery thickens...
233 setup_signal(int signr, sigfunc *shandler) {
234 struct sigaction nh, oh;
236 nh.sa_handler = shandler;
237 sigemptyset(&nh.sa_mask);
240 if(sigaction(signr, &nh, &oh) < 0)
248 if (uzbl.behave.fifo_dir)
249 unlink (uzbl.comm.fifo_path);
250 if (uzbl.behave.socket_dir)
251 unlink (uzbl.comm.socket_path);
253 g_free(uzbl.state.executable_path);
254 g_string_free(uzbl.state.keycmd, TRUE);
255 g_hash_table_destroy(uzbl.bindings);
256 g_hash_table_destroy(uzbl.behave.commands);
260 /* --- SIGNAL HANDLER --- */
263 catch_sigterm(int s) {
269 catch_sigint(int s) {
275 /* --- CALLBACKS --- */
278 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
281 (void) navigation_action;
282 (void) policy_decision;
284 const gchar* uri = webkit_network_request_get_uri (request);
285 if (uzbl.state.verbose)
286 printf("New window requested -> %s \n", uri);
287 new_window_load_uri(uri);
292 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
296 if (uzbl.state.selected_url != NULL) {
297 if (uzbl.state.verbose)
298 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
299 new_window_load_uri(uzbl.state.selected_url);
301 if (uzbl.state.verbose)
302 printf("New web view -> %s\n","Nothing to open, exiting");
308 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
311 if (uzbl.behave.download_handler) {
312 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
313 if (uzbl.state.verbose)
314 printf("Download -> %s\n",uri);
315 /* if urls not escaped, we may have to escape and quote uri before this call */
316 run_handler(uzbl.behave.download_handler, uri);
321 /* scroll a bar in a given direction */
323 scroll (GtkAdjustment* bar, GArray *argv) {
327 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
328 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
329 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
332 static void scroll_begin(WebKitWebView* page, GArray *argv) {
333 (void) page; (void) argv;
334 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
337 static void scroll_end(WebKitWebView* page, GArray *argv) {
338 (void) page; (void) argv;
339 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
340 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
343 static void scroll_vert(WebKitWebView* page, GArray *argv) {
345 scroll(uzbl.gui.bar_v, argv);
348 static void scroll_horz(WebKitWebView* page, GArray *argv) {
350 scroll(uzbl.gui.bar_h, argv);
355 if (!uzbl.behave.show_status) {
356 gtk_widget_hide(uzbl.gui.mainbar);
358 gtk_widget_show(uzbl.gui.mainbar);
364 toggle_status_cb (WebKitWebView* page, GArray *argv) {
368 if (uzbl.behave.show_status) {
369 gtk_widget_hide(uzbl.gui.mainbar);
371 gtk_widget_show(uzbl.gui.mainbar);
373 uzbl.behave.show_status = !uzbl.behave.show_status;
378 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
382 //Set selected_url state variable
383 g_free(uzbl.state.selected_url);
384 uzbl.state.selected_url = NULL;
386 uzbl.state.selected_url = g_strdup(link);
392 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
396 if (uzbl.gui.main_title)
397 g_free (uzbl.gui.main_title);
398 uzbl.gui.main_title = g_strdup (title);
403 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
406 uzbl.gui.sbar.load_progress = progress;
411 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
415 if (uzbl.behave.load_finish_handler)
416 run_handler(uzbl.behave.load_finish_handler, "");
420 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
424 if (uzbl.behave.load_start_handler)
425 run_handler(uzbl.behave.load_start_handler, "");
429 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
432 g_free (uzbl.state.uri);
433 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
434 uzbl.state.uri = g_string_free (newuri, FALSE);
435 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
436 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
439 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
440 if (uzbl.behave.load_commit_handler)
441 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
445 destroy_cb (GtkWidget* widget, gpointer data) {
453 if (uzbl.behave.history_handler) {
455 struct tm * timeinfo;
458 timeinfo = localtime ( &rawtime );
459 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
460 run_handler(uzbl.behave.history_handler, date);
465 /* VIEW funcs (little webkit wrappers) */
466 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
468 VIEWFUNC(reload_bypass_cache)
469 VIEWFUNC(stop_loading)
476 /* -- command to callback/function map for things we cannot attach to any signals */
478 static struct {char *name; Command command[2];} cmdlist[] =
479 { /* key function no_split */
480 { "back", {view_go_back, 0} },
481 { "forward", {view_go_forward, 0} },
482 { "scroll_vert", {scroll_vert, 0} },
483 { "scroll_horz", {scroll_horz, 0} },
484 { "scroll_begin", {scroll_begin, 0} },
485 { "scroll_end", {scroll_end, 0} },
486 { "reload", {view_reload, 0}, },
487 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
488 { "stop", {view_stop_loading, 0}, },
489 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
490 { "zoom_out", {view_zoom_out, 0}, },
491 { "uri", {load_uri, NOSPLIT} },
492 { "js", {run_js, NOSPLIT} },
493 { "script", {run_external_js, 0} },
494 { "toggle_status", {toggle_status_cb, 0} },
495 { "spawn", {spawn, 0} },
496 { "sh", {spawn_sh, 0} },
497 { "exit", {close_uzbl, 0} },
498 { "search", {search_forward_text, NOSPLIT} },
499 { "search_reverse", {search_reverse_text, NOSPLIT} },
500 { "toggle_insert_mode", {toggle_insert_mode, 0} },
501 { "runcmd", {runcmd, NOSPLIT} }
508 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
510 for (i = 0; i < LENGTH(cmdlist); i++)
511 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
514 /* -- CORE FUNCTIONS -- */
517 free_action(gpointer act) {
518 Action *action = (Action*)act;
519 g_free(action->name);
521 g_free(action->param);
526 new_action(const gchar *name, const gchar *param) {
527 Action *action = g_new(Action, 1);
529 action->name = g_strdup(name);
531 action->param = g_strdup(param);
533 action->param = NULL;
539 file_exists (const char * filename) {
540 return (access(filename, F_OK) == 0);
544 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
547 if (argv_idx(argv, 0)) {
548 if (strcmp (argv_idx(argv, 0), "0") == 0) {
549 uzbl.behave.insert_mode = FALSE;
551 uzbl.behave.insert_mode = TRUE;
554 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
561 load_uri (WebKitWebView *web_view, GArray *argv) {
562 if (argv_idx(argv, 0)) {
563 GString* newuri = g_string_new (argv_idx(argv, 0));
564 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
565 g_string_prepend (newuri, "http://");
566 /* if we do handle cookies, ask our handler for them */
567 webkit_web_view_load_uri (web_view, newuri->str);
568 g_string_free (newuri, TRUE);
573 run_js (WebKitWebView * web_view, GArray *argv) {
574 if (argv_idx(argv, 0))
575 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
579 run_external_js (WebKitWebView * web_view, GArray *argv) {
580 if (argv_idx(argv, 0)) {
581 GArray* lines = read_file_by_line (argv_idx (argv, 0));
586 while ((line = g_array_index(lines, gchar*, i))) {
588 js = g_strdup (line);
590 gchar* newjs = g_strconcat (js, line, NULL);
597 if (uzbl.state.verbose)
598 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
600 if (argv_idx (argv, 1)) {
601 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
605 webkit_web_view_execute_script (web_view, js);
607 g_array_free (lines, TRUE);
612 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
613 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
614 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
615 webkit_web_view_unmark_text_matches (page);
616 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
617 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
621 if (uzbl.state.searchtx) {
622 if (uzbl.state.verbose)
623 printf ("Searching: %s\n", uzbl.state.searchtx);
624 webkit_web_view_set_highlight_text_matches (page, TRUE);
625 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
630 search_forward_text (WebKitWebView *page, GArray *argv) {
631 search_text(page, argv, TRUE);
635 search_reverse_text (WebKitWebView *page, GArray *argv) {
636 search_text(page, argv, FALSE);
640 new_window_load_uri (const gchar * uri) {
641 GString* to_execute = g_string_new ("");
642 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
644 for (i = 0; entries[i].long_name != NULL; i++) {
645 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
646 gchar** str = (gchar**)entries[i].arg_data;
648 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
652 if (uzbl.state.verbose)
653 printf("\n%s\n", to_execute->str);
654 g_spawn_command_line_async (to_execute->str, NULL);
655 g_string_free (to_execute, TRUE);
659 close_uzbl (WebKitWebView *page, GArray *argv) {
665 /* --Statusbar functions-- */
667 build_progressbar_ascii(int percent) {
668 int width=uzbl.gui.sbar.progress_w;
671 GString *bar = g_string_new("");
673 l = (double)percent*((double)width/100.);
674 l = (int)(l+.5)>=(int)l ? l+.5 : l;
676 for(i=0; i<(int)l; i++)
677 g_string_append(bar, uzbl.gui.sbar.progress_s);
680 g_string_append(bar, uzbl.gui.sbar.progress_u);
682 return g_string_free(bar, FALSE);
687 const GScannerConfig scan_config = {
690 ) /* cset_skip_characters */,
695 ) /* cset_identifier_first */,
702 ) /* cset_identifier_nth */,
703 ( "" ) /* cpair_comment_single */,
705 TRUE /* case_sensitive */,
707 FALSE /* skip_comment_multi */,
708 FALSE /* skip_comment_single */,
709 FALSE /* scan_comment_multi */,
710 TRUE /* scan_identifier */,
711 TRUE /* scan_identifier_1char */,
712 FALSE /* scan_identifier_NULL */,
713 TRUE /* scan_symbols */,
714 FALSE /* scan_binary */,
715 FALSE /* scan_octal */,
716 FALSE /* scan_float */,
717 FALSE /* scan_hex */,
718 FALSE /* scan_hex_dollar */,
719 FALSE /* scan_string_sq */,
720 FALSE /* scan_string_dq */,
721 TRUE /* numbers_2_int */,
722 FALSE /* int_2_float */,
723 FALSE /* identifier_2_string */,
724 FALSE /* char_2_token */,
725 FALSE /* symbol_2_token */,
726 TRUE /* scope_0_fallback */,
731 uzbl.scan = g_scanner_new(&scan_config);
732 while(symp->symbol_name) {
733 g_scanner_scope_add_symbol(uzbl.scan, 0,
735 GINT_TO_POINTER(symp->symbol_token));
741 expand_template(const char *template) {
742 if(!template) return NULL;
744 GTokenType token = G_TOKEN_NONE;
745 GString *ret = g_string_new("");
749 g_scanner_input_text(uzbl.scan, template, strlen(template));
750 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
751 token = g_scanner_get_next_token(uzbl.scan);
753 if(token == G_TOKEN_SYMBOL) {
754 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
757 buf = uzbl.state.uri?
758 g_markup_printf_escaped("%s", uzbl.state.uri) :
760 g_string_append(ret, buf);
764 buf = itos(uzbl.gui.sbar.load_progress);
765 g_string_append(ret, buf);
768 case SYM_LOADPRGSBAR:
769 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
770 g_string_append(ret, buf);
774 buf = uzbl.gui.main_title?
775 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
777 g_string_append(ret, buf);
780 case SYM_SELECTED_URI:
781 buf = uzbl.state.selected_url?
782 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
784 g_string_append(ret, buf);
788 buf = itos(uzbl.xwin);
790 uzbl.state.instance_name?uzbl.state.instance_name:buf);
794 buf = uzbl.state.keycmd->str?
795 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
797 g_string_append(ret, buf);
802 uzbl.behave.insert_mode?"[I]":"[C]");
806 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
810 buf = itos(WEBKIT_MAJOR_VERSION);
811 g_string_append(ret, buf);
815 buf = itos(WEBKIT_MINOR_VERSION);
816 g_string_append(ret, buf);
820 buf = itos(WEBKIT_MICRO_VERSION);
821 g_string_append(ret, buf);
825 g_string_append(ret, uzbl.state.unameinfo.sysname);
828 g_string_append(ret, uzbl.state.unameinfo.nodename);
831 g_string_append(ret, uzbl.state.unameinfo.release);
834 g_string_append(ret, uzbl.state.unameinfo.version);
837 g_string_append(ret, uzbl.state.unameinfo.machine);
840 g_string_append(ret, ARCH);
844 g_string_append(ret, uzbl.state.unameinfo.domainname);
848 g_string_append(ret, COMMIT);
854 else if(token == G_TOKEN_INT) {
855 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
856 g_string_append(ret, buf);
859 else if(token == G_TOKEN_IDENTIFIER) {
860 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
862 else if(token == G_TOKEN_CHAR) {
863 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
867 return g_string_free(ret, FALSE);
869 /* --End Statusbar functions-- */
872 sharg_append(GArray *a, const gchar *str) {
873 const gchar *s = (str ? str : "");
874 g_array_append_val(a, s);
877 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
879 run_command (const gchar *command, const guint npre, const gchar **args,
880 const gboolean sync, char **stdout) {
881 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
884 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
885 gchar *pid = itos(getpid());
886 gchar *xwin = itos(uzbl.xwin);
888 sharg_append(a, command);
889 for (i = 0; i < npre; i++) /* add n args before the default vars */
890 sharg_append(a, args[i]);
891 sharg_append(a, uzbl.state.config_file);
892 sharg_append(a, pid);
893 sharg_append(a, xwin);
894 sharg_append(a, uzbl.comm.fifo_path);
895 sharg_append(a, uzbl.comm.socket_path);
896 sharg_append(a, uzbl.state.uri);
897 sharg_append(a, uzbl.gui.main_title);
899 for (i = npre; i < g_strv_length((gchar**)args); i++)
900 sharg_append(a, args[i]);
903 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
904 NULL, NULL, stdout, NULL, NULL, &err);
905 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
906 NULL, NULL, NULL, &err);
908 if (uzbl.state.verbose) {
909 GString *s = g_string_new("spawned:");
910 for (i = 0; i < (a->len); i++) {
911 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
912 g_string_append_printf(s, " %s", qarg);
915 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
916 printf("%s\n", s->str);
917 g_string_free(s, TRUE);
920 g_printerr("error on run_command: %s\n", err->message);
925 g_array_free (a, TRUE);
930 split_quoted(const gchar* src, const gboolean unquote) {
931 /* split on unquoted space, return array of strings;
932 remove a layer of quotes and backslashes if unquote */
933 if (!src) return NULL;
937 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
938 GString *s = g_string_new ("");
942 for (p = src; *p != '\0'; p++) {
943 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
944 else if (*p == '\\') { g_string_append_c(s, *p++);
945 g_string_append_c(s, *p); }
946 else if ((*p == '"') && unquote && !sq) dq = !dq;
947 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
949 else if ((*p == '\'') && unquote && !dq) sq = !sq;
950 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
952 else if ((*p == ' ') && !dq && !sq) {
953 dup = g_strdup(s->str);
954 g_array_append_val(a, dup);
955 g_string_truncate(s, 0);
956 } else g_string_append_c(s, *p);
958 dup = g_strdup(s->str);
959 g_array_append_val(a, dup);
960 ret = (gchar**)a->data;
961 g_array_free (a, FALSE);
962 g_string_free (s, TRUE);
967 spawn(WebKitWebView *web_view, GArray *argv) {
969 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
970 if (argv_idx(argv, 0))
971 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
975 spawn_sh(WebKitWebView *web_view, GArray *argv) {
977 if (!uzbl.behave.shell_cmd) {
978 g_printerr ("spawn_sh: shell_cmd is not set!\n");
983 gchar *spacer = g_strdup("");
984 g_array_insert_val(argv, 1, spacer);
985 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
987 for (i = 1; i < g_strv_length(cmd); i++)
988 g_array_prepend_val(argv, cmd[i]);
990 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
996 parse_command(const char *cmd, const char *param) {
999 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1002 gchar **par = split_quoted(param, TRUE);
1003 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1005 if (c[1] == NOSPLIT) { /* don't split */
1006 sharg_append(a, param);
1008 for (i = 0; i < g_strv_length(par); i++)
1009 sharg_append(a, par[i]);
1011 c[0](uzbl.gui.web_view, a);
1013 g_array_free (a, TRUE);
1016 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1019 /* command parser */
1022 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1023 G_REGEX_OPTIMIZE, 0, NULL);
1024 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1025 G_REGEX_OPTIMIZE, 0, NULL);
1026 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1027 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1028 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1029 G_REGEX_OPTIMIZE, 0, NULL);
1030 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1031 G_REGEX_OPTIMIZE, 0, NULL);
1035 get_var_value(gchar *name) {
1038 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1039 if(c->type == TYPE_STRING)
1040 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1041 else if(c->type == TYPE_INT)
1042 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1051 if(*uzbl.net.proxy_url == ' '
1052 || uzbl.net.proxy_url == NULL) {
1053 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1054 (GType) SOUP_SESSION_PROXY_URI);
1057 suri = soup_uri_new(uzbl.net.proxy_url);
1058 g_object_set(G_OBJECT(uzbl.net.soup_session),
1059 SOUP_SESSION_PROXY_URI,
1061 soup_uri_free(suri);
1068 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1069 g_array_append_val (a, uzbl.state.uri);
1070 load_uri(uzbl.gui.web_view, a);
1071 g_array_free (a, TRUE);
1075 cmd_always_insert_mode() {
1076 uzbl.behave.insert_mode =
1077 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1083 g_object_set(G_OBJECT(uzbl.net.soup_session),
1084 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1088 cmd_max_conns_host() {
1089 g_object_set(G_OBJECT(uzbl.net.soup_session),
1090 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1095 soup_session_remove_feature
1096 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1097 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1098 /*g_free(uzbl.net.soup_logger);*/
1100 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1101 soup_session_add_feature(uzbl.net.soup_session,
1102 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1106 cmd_default_font_size() {
1107 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1108 if (uzbl.behave.default_font_size > 0) {
1109 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1112 if (uzbl.behave.default_monospace_size > 0) {
1113 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1114 uzbl.behave.default_monospace_size, NULL);
1116 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1117 uzbl.behave.default_font_size, NULL);
1122 cmd_disable_plugins() {
1123 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1124 g_object_set (G_OBJECT(ws), "enable-plugins", !uzbl.behave.disable_plugins, NULL);
1128 cmd_minimum_font_size() {
1129 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1130 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1137 buf = init_fifo(uzbl.behave.fifo_dir);
1138 if(uzbl.behave.fifo_dir)
1139 g_free(uzbl.behave.fifo_dir);
1141 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1148 buf = init_socket(uzbl.behave.socket_dir);
1149 if(uzbl.behave.socket_dir)
1150 g_free(uzbl.behave.socket_dir);
1152 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1160 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1161 uzbl.behave.modmask = 0;
1163 if(uzbl.behave.modkey)
1164 g_free(uzbl.behave.modkey);
1165 uzbl.behave.modkey = buf;
1167 for (i = 0; modkeys[i].key != NULL; i++) {
1168 if (g_strrstr(buf, modkeys[i].key))
1169 uzbl.behave.modmask |= modkeys[i].mask;
1177 buf = set_useragent(uzbl.net.useragent);
1178 if(uzbl.net.useragent)
1179 g_free(uzbl.net.useragent);
1181 uzbl.net.useragent = buf?buf:g_strdup("");
1186 gtk_widget_ref(uzbl.gui.scrolled_win);
1187 gtk_widget_ref(uzbl.gui.mainbar);
1188 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1189 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1191 if(uzbl.behave.status_top) {
1192 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1193 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1196 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1197 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1199 gtk_widget_unref(uzbl.gui.scrolled_win);
1200 gtk_widget_unref(uzbl.gui.mainbar);
1201 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1206 set_var_value(gchar *name, gchar *val) {
1207 uzbl_cmdprop *c = NULL;
1210 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1211 /* check for the variable type */
1212 if (c->type == TYPE_STRING) {
1214 *c->ptr = g_strdup(val);
1215 } else if(c->type == TYPE_INT) {
1216 int *ip = GPOINTER_TO_INT(c->ptr);
1217 *ip = (int)strtoul(val, &endp, 10);
1220 /* invoke a command specific function */
1221 if(c->func) c->func();
1227 runcmd(WebKitWebView* page, GArray *argv) {
1229 parse_cmd_line(argv_idx(argv, 0));
1233 parse_cmd_line(const char *ctl_line) {
1237 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1238 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1239 if(tokens[0][0] == 0) {
1240 gchar* value = parseenv (tokens[2]);
1241 set_var_value(tokens[1], value);
1246 printf("Error in command: %s\n", tokens[0]);
1249 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1250 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1251 if(tokens[0][0] == 0) {
1252 get_var_value(tokens[1]);
1256 printf("Error in command: %s\n", tokens[0]);
1259 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1260 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1261 if(tokens[0][0] == 0) {
1262 gchar* value = parseenv (tokens[2]);
1263 add_binding(tokens[1], value);
1268 printf("Error in command: %s\n", tokens[0]);
1271 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1272 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1273 if(tokens[0][0] == 0) {
1274 parse_command(tokens[1], tokens[2]);
1278 printf("Error in command: %s\n", tokens[0]);
1280 /* KEYCMD command */
1281 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1282 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1283 if(tokens[0][0] == 0) {
1284 /* should incremental commands want each individual "keystroke"
1285 sent in a loop or the whole string in one go like now? */
1286 g_string_assign(uzbl.state.keycmd, tokens[1]);
1288 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1295 else if( (ctl_line[0] == '#')
1296 || (ctl_line[0] == ' ')
1297 || (ctl_line[0] == '\n'))
1298 ; /* ignore these lines */
1300 printf("Command not understood (%s)\n", ctl_line);
1306 build_stream_name(int type, const gchar* dir) {
1308 State *s = &uzbl.state;
1311 xwin_str = itos((int)uzbl.xwin);
1313 str = g_strdup_printf
1314 ("%s/uzbl_fifo_%s", dir,
1315 s->instance_name ? s->instance_name : xwin_str);
1316 } else if (type == SOCKET) {
1317 str = g_strdup_printf
1318 ("%s/uzbl_socket_%s", dir,
1319 s->instance_name ? s->instance_name : xwin_str );
1326 control_fifo(GIOChannel *gio, GIOCondition condition) {
1327 if (uzbl.state.verbose)
1328 printf("triggered\n");
1333 if (condition & G_IO_HUP)
1334 g_error ("Fifo: Read end of pipe died!\n");
1337 g_error ("Fifo: GIOChannel broke\n");
1339 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1340 if (ret == G_IO_STATUS_ERROR) {
1341 g_error ("Fifo: Error reading: %s\n", err->message);
1345 parse_cmd_line(ctl_line);
1352 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1353 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1354 if (unlink(uzbl.comm.fifo_path) == -1)
1355 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1356 g_free(uzbl.comm.fifo_path);
1357 uzbl.comm.fifo_path = NULL;
1360 if (*dir == ' ') { /* space unsets the variable */
1364 GIOChannel *chan = NULL;
1365 GError *error = NULL;
1366 gchar *path = build_stream_name(FIFO, dir);
1368 if (!file_exists(path)) {
1369 if (mkfifo (path, 0666) == 0) {
1370 // 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.
1371 chan = g_io_channel_new_file(path, "r+", &error);
1373 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1374 if (uzbl.state.verbose)
1375 printf ("init_fifo: created successfully as %s\n", path);
1376 uzbl.comm.fifo_path = path;
1378 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1379 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1380 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1381 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1383 /* if we got this far, there was an error; cleanup */
1384 if (error) g_error_free (error);
1390 control_stdin(GIOChannel *gio, GIOCondition condition) {
1392 gchar *ctl_line = NULL;
1395 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1396 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1399 parse_cmd_line(ctl_line);
1407 GIOChannel *chan = NULL;
1408 GError *error = NULL;
1410 chan = g_io_channel_unix_new(fileno(stdin));
1412 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1413 g_error ("Stdin: could not add watch\n");
1415 if (uzbl.state.verbose)
1416 printf ("Stdin: watch added successfully\n");
1419 g_error ("Stdin: Error while opening: %s\n", error->message);
1421 if (error) g_error_free (error);
1425 control_socket(GIOChannel *chan) {
1426 struct sockaddr_un remote;
1427 char buffer[512], *ctl_line;
1429 int sock, clientsock, n, done;
1432 sock = g_io_channel_unix_get_fd(chan);
1434 memset (buffer, 0, sizeof (buffer));
1436 t = sizeof (remote);
1437 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1441 memset (temp, 0, sizeof (temp));
1442 n = recv (clientsock, temp, 128, 0);
1444 buffer[strlen (buffer)] = '\0';
1448 strcat (buffer, temp);
1451 if (strcmp (buffer, "\n") < 0) {
1452 buffer[strlen (buffer) - 1] = '\0';
1454 buffer[strlen (buffer)] = '\0';
1457 ctl_line = g_strdup(buffer);
1458 parse_cmd_line (ctl_line);
1461 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1462 GError *error = NULL;
1465 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1466 if (ret == G_IO_STATUS_ERROR)
1467 g_error ("Error reading: %s\n", error->message);
1469 printf("Got line %s (%u bytes) \n",ctl_line, len);
1471 parse_line(ctl_line);
1479 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1480 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1481 if (unlink(uzbl.comm.socket_path) == -1)
1482 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1483 g_free(uzbl.comm.socket_path);
1484 uzbl.comm.socket_path = NULL;
1492 GIOChannel *chan = NULL;
1494 struct sockaddr_un local;
1495 gchar *path = build_stream_name(SOCKET, dir);
1497 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1499 local.sun_family = AF_UNIX;
1500 strcpy (local.sun_path, path);
1501 unlink (local.sun_path);
1503 len = strlen (local.sun_path) + sizeof (local.sun_family);
1504 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1505 if (uzbl.state.verbose)
1506 printf ("init_socket: opened in %s\n", path);
1509 if( (chan = g_io_channel_unix_new(sock)) ) {
1510 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1511 uzbl.comm.socket_path = path;
1514 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1516 /* if we got this far, there was an error; cleanup */
1523 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1524 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1526 // this function may be called very early when the templates are not set (yet), hence the checks
1528 update_title (void) {
1529 Behaviour *b = &uzbl.behave;
1532 if (b->show_status) {
1533 if (b->title_format_short) {
1534 parsed = expand_template(b->title_format_short);
1535 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1538 if (b->status_format) {
1539 parsed = expand_template(b->status_format);
1540 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1543 if (b->status_background) {
1545 gdk_color_parse (b->status_background, &color);
1546 //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)
1547 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1550 if (b->title_format_long) {
1551 parsed = expand_template(b->title_format_long);
1552 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1559 key_press_cb (GtkWidget* window, GdkEventKey* event)
1561 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1565 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1566 || 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)
1569 /* turn off insert mode (if always_insert_mode is not used) */
1570 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1571 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1576 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1579 if (event->keyval == GDK_Escape) {
1580 g_string_truncate(uzbl.state.keycmd, 0);
1585 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1586 if (event->keyval == GDK_Insert) {
1588 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1589 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1591 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1594 g_string_append (uzbl.state.keycmd, str);
1601 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1602 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1606 gboolean key_ret = FALSE;
1607 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1609 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1611 run_keycmd(key_ret);
1613 if (key_ret) return (!uzbl.behave.insert_mode);
1618 run_keycmd(const gboolean key_ret) {
1619 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1621 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1622 g_string_truncate(uzbl.state.keycmd, 0);
1623 parse_command(action->name, action->param);
1627 /* try if it's an incremental keycmd or one that takes args, and run it */
1628 GString* short_keys = g_string_new ("");
1629 GString* short_keys_inc = g_string_new ("");
1631 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1632 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1633 g_string_assign(short_keys_inc, short_keys->str);
1634 g_string_append_c(short_keys, '_');
1635 g_string_append_c(short_keys_inc, '*');
1637 gboolean exec_now = FALSE;
1638 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1639 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1640 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1641 if (key_ret) { /* just quit the incremental command on return */
1642 g_string_truncate(uzbl.state.keycmd, 0);
1644 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1648 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1649 GString* actionname = g_string_new ("");
1650 GString* actionparam = g_string_new ("");
1651 g_string_erase (parampart, 0, i+1);
1653 g_string_printf (actionname, action->name, parampart->str);
1655 g_string_printf (actionparam, action->param, parampart->str);
1656 parse_command(actionname->str, actionparam->str);
1657 g_string_free (actionname, TRUE);
1658 g_string_free (actionparam, TRUE);
1659 g_string_free (parampart, TRUE);
1661 g_string_truncate(uzbl.state.keycmd, 0);
1665 g_string_truncate(short_keys, short_keys->len - 1);
1667 g_string_free (short_keys, TRUE);
1668 g_string_free (short_keys_inc, TRUE);
1675 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1676 //main_window_ref = g_object_ref(scrolled_window);
1677 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
1679 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1680 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1682 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1683 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1684 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1685 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1686 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1687 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1688 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1689 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1690 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1691 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1693 return scrolled_window;
1700 g->mainbar = gtk_hbox_new (FALSE, 0);
1702 /* keep a reference to the bar so we can re-pack it at runtime*/
1703 //sbar_ref = g_object_ref(g->mainbar);
1705 g->mainbar_label = gtk_label_new ("");
1706 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1707 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1708 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1709 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1710 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1715 GtkWidget* create_window () {
1716 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1717 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1718 gtk_widget_set_name (window, "Uzbl browser");
1719 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1720 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1726 run_handler (const gchar *act, const gchar *args) {
1727 char **parts = g_strsplit(act, " ", 2);
1729 else if ((g_strcmp0(parts[0], "spawn") == 0)
1730 || (g_strcmp0(parts[0], "sh") == 0)) {
1732 GString *a = g_string_new ("");
1734 spawnparts = split_quoted(parts[1], FALSE);
1735 g_string_append_printf(a, "%s", spawnparts[0]);
1736 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1737 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1738 g_string_append_printf(a, " %s", spawnparts[i]);
1739 parse_command(parts[0], a->str);
1740 g_string_free (a, TRUE);
1741 g_strfreev (spawnparts);
1743 parse_command(parts[0], parts[1]);
1748 add_binding (const gchar *key, const gchar *act) {
1749 char **parts = g_strsplit(act, " ", 2);
1756 if (uzbl.state.verbose)
1757 printf ("Binding %-10s : %s\n", key, act);
1758 action = new_action(parts[0], parts[1]);
1760 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1765 get_xdg_var (XDG_Var xdg) {
1766 const gchar* actual_value = getenv (xdg.environmental);
1767 const gchar* home = getenv ("HOME");
1769 gchar* return_value = str_replace ("~", home, actual_value);
1771 if (! actual_value || strcmp (actual_value, "") == 0) {
1772 if (xdg.default_value) {
1773 return_value = str_replace ("~", home, xdg.default_value);
1775 return_value = NULL;
1778 return return_value;
1782 find_xdg_file (int xdg_type, char* filename) {
1783 /* xdg_type = 0 => config
1784 xdg_type = 1 => data
1785 xdg_type = 2 => cache*/
1787 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1788 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1791 gchar* temporary_string;
1795 if (! file_exists (temporary_file) && xdg_type != 2) {
1796 buf = get_xdg_var (XDG[3 + xdg_type]);
1797 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1800 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1801 g_free (temporary_file);
1802 temporary_file = g_strconcat (temporary_string, filename, NULL);
1806 //g_free (temporary_string); - segfaults.
1808 if (file_exists (temporary_file)) {
1809 return temporary_file;
1816 State *s = &uzbl.state;
1817 Network *n = &uzbl.net;
1819 for (i = 0; default_config[i].command != NULL; i++) {
1820 parse_cmd_line(default_config[i].command);
1823 if (!s->config_file) {
1824 s->config_file = find_xdg_file (0, "/uzbl/config");
1827 if (s->config_file) {
1828 GArray* lines = read_file_by_line (s->config_file);
1832 while ((line = g_array_index(lines, gchar*, i))) {
1833 parse_cmd_line (line);
1837 g_array_free (lines, TRUE);
1839 if (uzbl.state.verbose)
1840 printf ("No configuration file loaded.\n");
1843 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1847 set_useragent(gchar *val) {
1852 gchar *ua = expand_template(val);
1854 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1858 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1861 if (!uzbl.behave.cookie_handler) return;
1863 gchar * stdout = NULL;
1864 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1865 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1866 gchar *action = g_strdup ("GET");
1867 SoupURI * soup_uri = soup_message_get_uri(msg);
1868 sharg_append(a, action);
1869 sharg_append(a, soup_uri->host);
1870 sharg_append(a, soup_uri->path);
1871 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1872 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1874 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1877 g_array_free(a, TRUE);
1881 save_cookies (SoupMessage *msg, gpointer user_data){
1885 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1886 cookie = soup_cookie_to_set_cookie_header(ck->data);
1887 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1888 SoupURI * soup_uri = soup_message_get_uri(msg);
1889 gchar *action = strdup("PUT");
1890 sharg_append(a, action);
1891 sharg_append(a, soup_uri->host);
1892 sharg_append(a, soup_uri->path);
1893 sharg_append(a, cookie);
1894 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1897 g_array_free(a, TRUE);
1903 main (int argc, char* argv[]) {
1904 gtk_init (&argc, &argv);
1905 if (!g_thread_supported ())
1906 g_thread_init (NULL);
1907 uzbl.state.executable_path = g_strdup(argv[0]);
1908 uzbl.state.selected_url = NULL;
1909 uzbl.state.searchtx = NULL;
1911 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1912 g_option_context_add_main_entries (context, entries, NULL);
1913 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1914 g_option_context_parse (context, &argc, &argv, NULL);
1915 g_option_context_free(context);
1916 /* initialize hash table */
1917 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1919 uzbl.net.soup_session = webkit_get_default_session();
1920 uzbl.state.keycmd = g_string_new("");
1922 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1923 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1924 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1925 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1927 if(uname(&uzbl.state.unameinfo) == -1)
1928 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1930 uzbl.gui.sbar.progress_s = g_strdup("=");
1931 uzbl.gui.sbar.progress_u = g_strdup("ยท");
1932 uzbl.gui.sbar.progress_w = 10;
1937 make_var_to_name_hash();
1939 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1941 uzbl.gui.scrolled_win = create_browser();
1944 /* initial packing */
1945 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1946 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1948 uzbl.gui.main_window = create_window ();
1949 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1952 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1953 gtk_widget_show_all (uzbl.gui.main_window);
1954 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1956 if (uzbl.state.verbose) {
1957 printf("Uzbl start location: %s\n", argv[0]);
1958 printf("window_id %i\n",(int) uzbl.xwin);
1959 printf("pid %i\n", getpid ());
1960 printf("name: %s\n", uzbl.state.instance_name);
1963 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1964 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1965 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1966 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1967 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1971 if (!uzbl.behave.show_status)
1972 gtk_widget_hide(uzbl.gui.mainbar);
1978 if(uzbl.state.uri) {
1979 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1980 g_array_append_val(a, uzbl.state.uri);
1981 load_uri (uzbl.gui.web_view, a);
1982 g_array_free (a, TRUE);
1988 return EXIT_SUCCESS;
1991 /* vi: set et ts=4: */