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 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
616 if (uzbl.state.searchtx != NULL) {
617 if (uzbl.state.verbose)
618 printf ("Searching: %s\n", uzbl.state.searchtx);
620 if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
621 webkit_web_view_unmark_text_matches (page);
622 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
624 if (uzbl.state.searchold != NULL)
625 g_free (uzbl.state.searchold);
627 uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
630 webkit_web_view_set_highlight_text_matches (page, TRUE);
631 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
632 g_free(uzbl.state.searchtx);
633 uzbl.state.searchtx = NULL;
638 search_forward_text (WebKitWebView *page, GArray *argv) {
639 search_text(page, argv, TRUE);
643 search_reverse_text (WebKitWebView *page, GArray *argv) {
644 search_text(page, argv, FALSE);
648 new_window_load_uri (const gchar * uri) {
649 GString* to_execute = g_string_new ("");
650 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
652 for (i = 0; entries[i].long_name != NULL; i++) {
653 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
654 gchar** str = (gchar**)entries[i].arg_data;
656 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
660 if (uzbl.state.verbose)
661 printf("\n%s\n", to_execute->str);
662 g_spawn_command_line_async (to_execute->str, NULL);
663 g_string_free (to_execute, TRUE);
667 close_uzbl (WebKitWebView *page, GArray *argv) {
673 /* --Statusbar functions-- */
675 build_progressbar_ascii(int percent) {
676 int width=uzbl.gui.sbar.progress_w;
679 GString *bar = g_string_new("");
681 l = (double)percent*((double)width/100.);
682 l = (int)(l+.5)>=(int)l ? l+.5 : l;
684 for(i=0; i<(int)l; i++)
685 g_string_append(bar, uzbl.gui.sbar.progress_s);
688 g_string_append(bar, uzbl.gui.sbar.progress_u);
690 return g_string_free(bar, FALSE);
695 const GScannerConfig scan_config = {
698 ) /* cset_skip_characters */,
703 ) /* cset_identifier_first */,
710 ) /* cset_identifier_nth */,
711 ( "" ) /* cpair_comment_single */,
713 TRUE /* case_sensitive */,
715 FALSE /* skip_comment_multi */,
716 FALSE /* skip_comment_single */,
717 FALSE /* scan_comment_multi */,
718 TRUE /* scan_identifier */,
719 TRUE /* scan_identifier_1char */,
720 FALSE /* scan_identifier_NULL */,
721 TRUE /* scan_symbols */,
722 FALSE /* scan_binary */,
723 FALSE /* scan_octal */,
724 FALSE /* scan_float */,
725 FALSE /* scan_hex */,
726 FALSE /* scan_hex_dollar */,
727 FALSE /* scan_string_sq */,
728 FALSE /* scan_string_dq */,
729 TRUE /* numbers_2_int */,
730 FALSE /* int_2_float */,
731 FALSE /* identifier_2_string */,
732 FALSE /* char_2_token */,
733 FALSE /* symbol_2_token */,
734 TRUE /* scope_0_fallback */,
739 uzbl.scan = g_scanner_new(&scan_config);
740 while(symp->symbol_name) {
741 g_scanner_scope_add_symbol(uzbl.scan, 0,
743 GINT_TO_POINTER(symp->symbol_token));
749 expand_template(const char *template) {
750 if(!template) return NULL;
752 GTokenType token = G_TOKEN_NONE;
753 GString *ret = g_string_new("");
757 g_scanner_input_text(uzbl.scan, template, strlen(template));
758 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
759 token = g_scanner_get_next_token(uzbl.scan);
761 if(token == G_TOKEN_SYMBOL) {
762 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
765 buf = uzbl.state.uri?
766 g_markup_printf_escaped("%s", uzbl.state.uri) :
768 g_string_append(ret, buf);
772 buf = itos(uzbl.gui.sbar.load_progress);
773 g_string_append(ret, buf);
776 case SYM_LOADPRGSBAR:
777 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
778 g_string_append(ret, buf);
782 buf = uzbl.gui.main_title?
783 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
785 g_string_append(ret, buf);
788 case SYM_SELECTED_URI:
789 buf = uzbl.state.selected_url?
790 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
792 g_string_append(ret, buf);
796 buf = itos(uzbl.xwin);
798 uzbl.state.instance_name?uzbl.state.instance_name:buf);
802 buf = uzbl.state.keycmd->str?
803 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
805 g_string_append(ret, buf);
810 uzbl.behave.insert_mode?"[I]":"[C]");
814 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
818 buf = itos(WEBKIT_MAJOR_VERSION);
819 g_string_append(ret, buf);
823 buf = itos(WEBKIT_MINOR_VERSION);
824 g_string_append(ret, buf);
828 buf = itos(WEBKIT_MICRO_VERSION);
829 g_string_append(ret, buf);
833 g_string_append(ret, uzbl.state.unameinfo.sysname);
836 g_string_append(ret, uzbl.state.unameinfo.nodename);
839 g_string_append(ret, uzbl.state.unameinfo.release);
842 g_string_append(ret, uzbl.state.unameinfo.version);
845 g_string_append(ret, uzbl.state.unameinfo.machine);
848 g_string_append(ret, ARCH);
852 g_string_append(ret, uzbl.state.unameinfo.domainname);
856 g_string_append(ret, COMMIT);
862 else if(token == G_TOKEN_INT) {
863 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
864 g_string_append(ret, buf);
867 else if(token == G_TOKEN_IDENTIFIER) {
868 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
870 else if(token == G_TOKEN_CHAR) {
871 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
875 return g_string_free(ret, FALSE);
877 /* --End Statusbar functions-- */
880 sharg_append(GArray *a, const gchar *str) {
881 const gchar *s = (str ? str : "");
882 g_array_append_val(a, s);
885 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
887 run_command (const gchar *command, const guint npre, const gchar **args,
888 const gboolean sync, char **stdout) {
889 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
892 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
893 gchar *pid = itos(getpid());
894 gchar *xwin = itos(uzbl.xwin);
896 sharg_append(a, command);
897 for (i = 0; i < npre; i++) /* add n args before the default vars */
898 sharg_append(a, args[i]);
899 sharg_append(a, uzbl.state.config_file);
900 sharg_append(a, pid);
901 sharg_append(a, xwin);
902 sharg_append(a, uzbl.comm.fifo_path);
903 sharg_append(a, uzbl.comm.socket_path);
904 sharg_append(a, uzbl.state.uri);
905 sharg_append(a, uzbl.gui.main_title);
907 for (i = npre; i < g_strv_length((gchar**)args); i++)
908 sharg_append(a, args[i]);
911 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
912 NULL, NULL, stdout, NULL, NULL, &err);
913 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
914 NULL, NULL, NULL, &err);
916 if (uzbl.state.verbose) {
917 GString *s = g_string_new("spawned:");
918 for (i = 0; i < (a->len); i++) {
919 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
920 g_string_append_printf(s, " %s", qarg);
923 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
924 printf("%s\n", s->str);
925 g_string_free(s, TRUE);
928 g_printerr("error on run_command: %s\n", err->message);
933 g_array_free (a, TRUE);
938 split_quoted(const gchar* src, const gboolean unquote) {
939 /* split on unquoted space, return array of strings;
940 remove a layer of quotes and backslashes if unquote */
941 if (!src) return NULL;
945 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
946 GString *s = g_string_new ("");
950 for (p = src; *p != '\0'; p++) {
951 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
952 else if (*p == '\\') { g_string_append_c(s, *p++);
953 g_string_append_c(s, *p); }
954 else if ((*p == '"') && unquote && !sq) dq = !dq;
955 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
957 else if ((*p == '\'') && unquote && !dq) sq = !sq;
958 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
960 else if ((*p == ' ') && !dq && !sq) {
961 dup = g_strdup(s->str);
962 g_array_append_val(a, dup);
963 g_string_truncate(s, 0);
964 } else g_string_append_c(s, *p);
966 dup = g_strdup(s->str);
967 g_array_append_val(a, dup);
968 ret = (gchar**)a->data;
969 g_array_free (a, FALSE);
970 g_string_free (s, TRUE);
975 spawn(WebKitWebView *web_view, GArray *argv) {
977 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
978 if (argv_idx(argv, 0))
979 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
983 spawn_sh(WebKitWebView *web_view, GArray *argv) {
985 if (!uzbl.behave.shell_cmd) {
986 g_printerr ("spawn_sh: shell_cmd is not set!\n");
991 gchar *spacer = g_strdup("");
992 g_array_insert_val(argv, 1, spacer);
993 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
995 for (i = 1; i < g_strv_length(cmd); i++)
996 g_array_prepend_val(argv, cmd[i]);
998 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1004 parse_command(const char *cmd, const char *param) {
1007 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1010 gchar **par = split_quoted(param, TRUE);
1011 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1013 if (c[1] == NOSPLIT) { /* don't split */
1014 sharg_append(a, param);
1016 for (i = 0; i < g_strv_length(par); i++)
1017 sharg_append(a, par[i]);
1019 c[0](uzbl.gui.web_view, a);
1021 g_array_free (a, TRUE);
1024 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1027 /* command parser */
1030 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1031 G_REGEX_OPTIMIZE, 0, NULL);
1032 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1033 G_REGEX_OPTIMIZE, 0, NULL);
1034 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1035 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1036 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1037 G_REGEX_OPTIMIZE, 0, NULL);
1038 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1039 G_REGEX_OPTIMIZE, 0, NULL);
1043 get_var_value(gchar *name) {
1046 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1047 if(c->type == TYPE_STRING)
1048 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1049 else if(c->type == TYPE_INT)
1050 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1059 if(*uzbl.net.proxy_url == ' '
1060 || uzbl.net.proxy_url == NULL) {
1061 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1062 (GType) SOUP_SESSION_PROXY_URI);
1065 suri = soup_uri_new(uzbl.net.proxy_url);
1066 g_object_set(G_OBJECT(uzbl.net.soup_session),
1067 SOUP_SESSION_PROXY_URI,
1069 soup_uri_free(suri);
1076 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1077 g_array_append_val (a, uzbl.state.uri);
1078 load_uri(uzbl.gui.web_view, a);
1079 g_array_free (a, TRUE);
1083 cmd_always_insert_mode() {
1084 uzbl.behave.insert_mode =
1085 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1091 g_object_set(G_OBJECT(uzbl.net.soup_session),
1092 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1096 cmd_max_conns_host() {
1097 g_object_set(G_OBJECT(uzbl.net.soup_session),
1098 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1103 soup_session_remove_feature
1104 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1105 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1106 /*g_free(uzbl.net.soup_logger);*/
1108 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1109 soup_session_add_feature(uzbl.net.soup_session,
1110 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1114 cmd_default_font_size() {
1115 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1116 if (uzbl.behave.default_font_size > 0) {
1117 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1120 if (uzbl.behave.default_monospace_size > 0) {
1121 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1122 uzbl.behave.default_monospace_size, NULL);
1124 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1125 uzbl.behave.default_font_size, NULL);
1130 cmd_disable_plugins() {
1131 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1132 g_object_set (G_OBJECT(ws), "enable-plugins", !uzbl.behave.disable_plugins, NULL);
1136 cmd_minimum_font_size() {
1137 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1138 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1145 buf = init_fifo(uzbl.behave.fifo_dir);
1146 if(uzbl.behave.fifo_dir)
1147 g_free(uzbl.behave.fifo_dir);
1149 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1156 buf = init_socket(uzbl.behave.socket_dir);
1157 if(uzbl.behave.socket_dir)
1158 g_free(uzbl.behave.socket_dir);
1160 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1168 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1169 uzbl.behave.modmask = 0;
1171 if(uzbl.behave.modkey)
1172 g_free(uzbl.behave.modkey);
1173 uzbl.behave.modkey = buf;
1175 for (i = 0; modkeys[i].key != NULL; i++) {
1176 if (g_strrstr(buf, modkeys[i].key))
1177 uzbl.behave.modmask |= modkeys[i].mask;
1185 buf = set_useragent(uzbl.net.useragent);
1186 if(uzbl.net.useragent)
1187 g_free(uzbl.net.useragent);
1189 uzbl.net.useragent = buf?buf:g_strdup("");
1194 gtk_widget_ref(uzbl.gui.scrolled_win);
1195 gtk_widget_ref(uzbl.gui.mainbar);
1196 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1197 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1199 if(uzbl.behave.status_top) {
1200 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1201 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1204 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1205 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1207 gtk_widget_unref(uzbl.gui.scrolled_win);
1208 gtk_widget_unref(uzbl.gui.mainbar);
1209 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1214 set_var_value(gchar *name, gchar *val) {
1215 uzbl_cmdprop *c = NULL;
1218 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1219 /* check for the variable type */
1220 if (c->type == TYPE_STRING) {
1222 *c->ptr = g_strdup(val);
1223 } else if(c->type == TYPE_INT) {
1224 int *ip = GPOINTER_TO_INT(c->ptr);
1225 *ip = (int)strtoul(val, &endp, 10);
1228 /* invoke a command specific function */
1229 if(c->func) c->func();
1235 runcmd(WebKitWebView* page, GArray *argv) {
1237 parse_cmd_line(argv_idx(argv, 0));
1241 parse_cmd_line(const char *ctl_line) {
1245 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1246 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1247 if(tokens[0][0] == 0) {
1248 gchar* value = parseenv (tokens[2]);
1249 set_var_value(tokens[1], value);
1254 printf("Error in command: %s\n", tokens[0]);
1257 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1258 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1259 if(tokens[0][0] == 0) {
1260 get_var_value(tokens[1]);
1264 printf("Error in command: %s\n", tokens[0]);
1267 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1268 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1269 if(tokens[0][0] == 0) {
1270 gchar* value = parseenv (tokens[2]);
1271 add_binding(tokens[1], value);
1276 printf("Error in command: %s\n", tokens[0]);
1279 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1280 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1281 if(tokens[0][0] == 0) {
1282 parse_command(tokens[1], tokens[2]);
1286 printf("Error in command: %s\n", tokens[0]);
1288 /* KEYCMD command */
1289 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1290 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1291 if(tokens[0][0] == 0) {
1292 /* should incremental commands want each individual "keystroke"
1293 sent in a loop or the whole string in one go like now? */
1294 g_string_assign(uzbl.state.keycmd, tokens[1]);
1296 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1303 else if( (ctl_line[0] == '#')
1304 || (ctl_line[0] == ' ')
1305 || (ctl_line[0] == '\n'))
1306 ; /* ignore these lines */
1308 printf("Command not understood (%s)\n", ctl_line);
1314 build_stream_name(int type, const gchar* dir) {
1316 State *s = &uzbl.state;
1319 xwin_str = itos((int)uzbl.xwin);
1321 str = g_strdup_printf
1322 ("%s/uzbl_fifo_%s", dir,
1323 s->instance_name ? s->instance_name : xwin_str);
1324 } else if (type == SOCKET) {
1325 str = g_strdup_printf
1326 ("%s/uzbl_socket_%s", dir,
1327 s->instance_name ? s->instance_name : xwin_str );
1334 control_fifo(GIOChannel *gio, GIOCondition condition) {
1335 if (uzbl.state.verbose)
1336 printf("triggered\n");
1341 if (condition & G_IO_HUP)
1342 g_error ("Fifo: Read end of pipe died!\n");
1345 g_error ("Fifo: GIOChannel broke\n");
1347 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1348 if (ret == G_IO_STATUS_ERROR) {
1349 g_error ("Fifo: Error reading: %s\n", err->message);
1353 parse_cmd_line(ctl_line);
1360 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1361 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1362 if (unlink(uzbl.comm.fifo_path) == -1)
1363 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1364 g_free(uzbl.comm.fifo_path);
1365 uzbl.comm.fifo_path = NULL;
1368 if (*dir == ' ') { /* space unsets the variable */
1372 GIOChannel *chan = NULL;
1373 GError *error = NULL;
1374 gchar *path = build_stream_name(FIFO, dir);
1376 if (!file_exists(path)) {
1377 if (mkfifo (path, 0666) == 0) {
1378 // 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.
1379 chan = g_io_channel_new_file(path, "r+", &error);
1381 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1382 if (uzbl.state.verbose)
1383 printf ("init_fifo: created successfully as %s\n", path);
1384 uzbl.comm.fifo_path = path;
1386 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1387 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1388 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1389 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1391 /* if we got this far, there was an error; cleanup */
1392 if (error) g_error_free (error);
1398 control_stdin(GIOChannel *gio, GIOCondition condition) {
1400 gchar *ctl_line = NULL;
1403 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1404 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1407 parse_cmd_line(ctl_line);
1415 GIOChannel *chan = NULL;
1416 GError *error = NULL;
1418 chan = g_io_channel_unix_new(fileno(stdin));
1420 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1421 g_error ("Stdin: could not add watch\n");
1423 if (uzbl.state.verbose)
1424 printf ("Stdin: watch added successfully\n");
1427 g_error ("Stdin: Error while opening: %s\n", error->message);
1429 if (error) g_error_free (error);
1433 control_socket(GIOChannel *chan) {
1434 struct sockaddr_un remote;
1435 char buffer[512], *ctl_line;
1437 int sock, clientsock, n, done;
1440 sock = g_io_channel_unix_get_fd(chan);
1442 memset (buffer, 0, sizeof (buffer));
1444 t = sizeof (remote);
1445 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1449 memset (temp, 0, sizeof (temp));
1450 n = recv (clientsock, temp, 128, 0);
1452 buffer[strlen (buffer)] = '\0';
1456 strcat (buffer, temp);
1459 if (strcmp (buffer, "\n") < 0) {
1460 buffer[strlen (buffer) - 1] = '\0';
1462 buffer[strlen (buffer)] = '\0';
1465 ctl_line = g_strdup(buffer);
1466 parse_cmd_line (ctl_line);
1469 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1470 GError *error = NULL;
1473 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1474 if (ret == G_IO_STATUS_ERROR)
1475 g_error ("Error reading: %s\n", error->message);
1477 printf("Got line %s (%u bytes) \n",ctl_line, len);
1479 parse_line(ctl_line);
1487 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1488 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1489 if (unlink(uzbl.comm.socket_path) == -1)
1490 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1491 g_free(uzbl.comm.socket_path);
1492 uzbl.comm.socket_path = NULL;
1500 GIOChannel *chan = NULL;
1502 struct sockaddr_un local;
1503 gchar *path = build_stream_name(SOCKET, dir);
1505 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1507 local.sun_family = AF_UNIX;
1508 strcpy (local.sun_path, path);
1509 unlink (local.sun_path);
1511 len = strlen (local.sun_path) + sizeof (local.sun_family);
1512 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1513 if (uzbl.state.verbose)
1514 printf ("init_socket: opened in %s\n", path);
1517 if( (chan = g_io_channel_unix_new(sock)) ) {
1518 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1519 uzbl.comm.socket_path = path;
1522 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1524 /* if we got this far, there was an error; cleanup */
1531 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1532 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1534 // this function may be called very early when the templates are not set (yet), hence the checks
1536 update_title (void) {
1537 Behaviour *b = &uzbl.behave;
1540 if (b->show_status) {
1541 if (b->title_format_short) {
1542 parsed = expand_template(b->title_format_short);
1543 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1546 if (b->status_format) {
1547 parsed = expand_template(b->status_format);
1548 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1551 if (b->status_background) {
1553 gdk_color_parse (b->status_background, &color);
1554 //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)
1555 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1558 if (b->title_format_long) {
1559 parsed = expand_template(b->title_format_long);
1560 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1567 key_press_cb (GtkWidget* window, GdkEventKey* event)
1569 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1573 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1574 || 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)
1577 /* turn off insert mode (if always_insert_mode is not used) */
1578 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1579 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1584 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1587 if (event->keyval == GDK_Escape) {
1588 g_string_truncate(uzbl.state.keycmd, 0);
1593 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1594 if (event->keyval == GDK_Insert) {
1596 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1597 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1599 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1602 g_string_append (uzbl.state.keycmd, str);
1609 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1610 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1614 gboolean key_ret = FALSE;
1615 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1617 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1619 run_keycmd(key_ret);
1621 if (key_ret) return (!uzbl.behave.insert_mode);
1626 run_keycmd(const gboolean key_ret) {
1627 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1629 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1630 g_string_truncate(uzbl.state.keycmd, 0);
1631 parse_command(action->name, action->param);
1635 /* try if it's an incremental keycmd or one that takes args, and run it */
1636 GString* short_keys = g_string_new ("");
1637 GString* short_keys_inc = g_string_new ("");
1639 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1640 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1641 g_string_assign(short_keys_inc, short_keys->str);
1642 g_string_append_c(short_keys, '_');
1643 g_string_append_c(short_keys_inc, '*');
1645 gboolean exec_now = FALSE;
1646 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1647 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1648 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1649 if (key_ret) { /* just quit the incremental command on return */
1650 g_string_truncate(uzbl.state.keycmd, 0);
1652 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1656 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1657 GString* actionname = g_string_new ("");
1658 GString* actionparam = g_string_new ("");
1659 g_string_erase (parampart, 0, i+1);
1661 g_string_printf (actionname, action->name, parampart->str);
1663 g_string_printf (actionparam, action->param, parampart->str);
1664 parse_command(actionname->str, actionparam->str);
1665 g_string_free (actionname, TRUE);
1666 g_string_free (actionparam, TRUE);
1667 g_string_free (parampart, TRUE);
1669 g_string_truncate(uzbl.state.keycmd, 0);
1673 g_string_truncate(short_keys, short_keys->len - 1);
1675 g_string_free (short_keys, TRUE);
1676 g_string_free (short_keys_inc, TRUE);
1683 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1684 //main_window_ref = g_object_ref(scrolled_window);
1685 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
1687 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1688 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1690 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1691 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1692 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1693 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1694 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1695 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1696 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1697 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1698 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1699 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1701 return scrolled_window;
1708 g->mainbar = gtk_hbox_new (FALSE, 0);
1710 /* keep a reference to the bar so we can re-pack it at runtime*/
1711 //sbar_ref = g_object_ref(g->mainbar);
1713 g->mainbar_label = gtk_label_new ("");
1714 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1715 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1716 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1717 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1718 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1723 GtkWidget* create_window () {
1724 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1725 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1726 gtk_widget_set_name (window, "Uzbl browser");
1727 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1728 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1734 run_handler (const gchar *act, const gchar *args) {
1735 char **parts = g_strsplit(act, " ", 2);
1737 else if ((g_strcmp0(parts[0], "spawn") == 0)
1738 || (g_strcmp0(parts[0], "sh") == 0)) {
1740 GString *a = g_string_new ("");
1742 spawnparts = split_quoted(parts[1], FALSE);
1743 g_string_append_printf(a, "%s", spawnparts[0]);
1744 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1745 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1746 g_string_append_printf(a, " %s", spawnparts[i]);
1747 parse_command(parts[0], a->str);
1748 g_string_free (a, TRUE);
1749 g_strfreev (spawnparts);
1751 parse_command(parts[0], parts[1]);
1756 add_binding (const gchar *key, const gchar *act) {
1757 char **parts = g_strsplit(act, " ", 2);
1764 if (uzbl.state.verbose)
1765 printf ("Binding %-10s : %s\n", key, act);
1766 action = new_action(parts[0], parts[1]);
1768 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1773 get_xdg_var (XDG_Var xdg) {
1774 const gchar* actual_value = getenv (xdg.environmental);
1775 const gchar* home = getenv ("HOME");
1777 gchar* return_value = str_replace ("~", home, actual_value);
1779 if (! actual_value || strcmp (actual_value, "") == 0) {
1780 if (xdg.default_value) {
1781 return_value = str_replace ("~", home, xdg.default_value);
1783 return_value = NULL;
1786 return return_value;
1790 find_xdg_file (int xdg_type, char* filename) {
1791 /* xdg_type = 0 => config
1792 xdg_type = 1 => data
1793 xdg_type = 2 => cache*/
1795 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1796 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1799 gchar* temporary_string;
1803 if (! file_exists (temporary_file) && xdg_type != 2) {
1804 buf = get_xdg_var (XDG[3 + xdg_type]);
1805 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1808 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1809 g_free (temporary_file);
1810 temporary_file = g_strconcat (temporary_string, filename, NULL);
1814 //g_free (temporary_string); - segfaults.
1816 if (file_exists (temporary_file)) {
1817 return temporary_file;
1824 State *s = &uzbl.state;
1825 Network *n = &uzbl.net;
1827 for (i = 0; default_config[i].command != NULL; i++) {
1828 parse_cmd_line(default_config[i].command);
1831 if (!s->config_file) {
1832 s->config_file = find_xdg_file (0, "/uzbl/config");
1835 if (s->config_file) {
1836 GArray* lines = read_file_by_line (s->config_file);
1840 while ((line = g_array_index(lines, gchar*, i))) {
1841 parse_cmd_line (line);
1845 g_array_free (lines, TRUE);
1847 if (uzbl.state.verbose)
1848 printf ("No configuration file loaded.\n");
1851 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1855 set_useragent(gchar *val) {
1860 gchar *ua = expand_template(val);
1862 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1866 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1869 if (!uzbl.behave.cookie_handler) return;
1871 gchar * stdout = NULL;
1872 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1873 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1874 gchar *action = g_strdup ("GET");
1875 SoupURI * soup_uri = soup_message_get_uri(msg);
1876 sharg_append(a, action);
1877 sharg_append(a, soup_uri->host);
1878 sharg_append(a, soup_uri->path);
1879 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1880 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1882 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1885 g_array_free(a, TRUE);
1889 save_cookies (SoupMessage *msg, gpointer user_data){
1893 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1894 cookie = soup_cookie_to_set_cookie_header(ck->data);
1895 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1896 SoupURI * soup_uri = soup_message_get_uri(msg);
1897 gchar *action = strdup("PUT");
1898 sharg_append(a, action);
1899 sharg_append(a, soup_uri->host);
1900 sharg_append(a, soup_uri->path);
1901 sharg_append(a, cookie);
1902 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1905 g_array_free(a, TRUE);
1911 main (int argc, char* argv[]) {
1912 gtk_init (&argc, &argv);
1913 if (!g_thread_supported ())
1914 g_thread_init (NULL);
1915 uzbl.state.executable_path = g_strdup(argv[0]);
1916 uzbl.state.selected_url = NULL;
1917 uzbl.state.searchtx = NULL;
1919 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1920 g_option_context_add_main_entries (context, entries, NULL);
1921 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1922 g_option_context_parse (context, &argc, &argv, NULL);
1923 g_option_context_free(context);
1924 /* initialize hash table */
1925 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1927 uzbl.net.soup_session = webkit_get_default_session();
1928 uzbl.state.keycmd = g_string_new("");
1930 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1931 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1932 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1933 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1935 if(uname(&uzbl.state.unameinfo) == -1)
1936 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1938 uzbl.gui.sbar.progress_s = g_strdup("=");
1939 uzbl.gui.sbar.progress_u = g_strdup("ยท");
1940 uzbl.gui.sbar.progress_w = 10;
1945 make_var_to_name_hash();
1947 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1949 uzbl.gui.scrolled_win = create_browser();
1952 /* initial packing */
1953 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1954 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1956 uzbl.gui.main_window = create_window ();
1957 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1960 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1961 gtk_widget_show_all (uzbl.gui.main_window);
1962 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1964 if (uzbl.state.verbose) {
1965 printf("Uzbl start location: %s\n", argv[0]);
1966 printf("window_id %i\n",(int) uzbl.xwin);
1967 printf("pid %i\n", getpid ());
1968 printf("name: %s\n", uzbl.state.instance_name);
1971 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1972 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1973 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1974 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1975 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1979 if (!uzbl.behave.show_status)
1980 gtk_widget_hide(uzbl.gui.mainbar);
1986 if(uzbl.state.uri) {
1987 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1988 g_array_append_val(a, uzbl.state.uri);
1989 load_uri (uzbl.gui.web_view, a);
1990 g_array_free (a, TRUE);
1996 return EXIT_SUCCESS;
1999 /* vi: set et ts=4: */