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>
61 /* define names and pointers to all config specific variables */
62 typedef const struct {
68 enum {TYPE_INT, TYPE_STRING};
73 } var_name_to_ptr[] = {
74 /* variable name pointer to variable in code variable type callback function */
75 /* ------------------------------------------------------------------------------------------------------------------- */
76 { "uri", {.ptr = (void *)&uzbl.state.uri, .type = TYPE_STRING, .func = cmd_load_uri}},
77 { "status_message", {.ptr = (void *)&uzbl.gui.sbar.msg, .type = TYPE_STRING, .func = update_title}},
78 { "show_status", {.ptr = (void *)&uzbl.behave.show_status, .type = TYPE_INT, .func = cmd_set_status}},
79 { "status_top", {.ptr = (void *)&uzbl.behave.status_top, .type = TYPE_INT, .func = move_statusbar}},
80 { "status_format", {.ptr = (void *)&uzbl.behave.status_format, .type = TYPE_STRING, .func = update_title}},
81 { "status_background", {.ptr = (void *)&uzbl.behave.status_background, .type = TYPE_STRING, .func = update_title}},
82 { "title_format_long", {.ptr = (void *)&uzbl.behave.title_format_long, .type = TYPE_STRING, .func = update_title}},
83 { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short, .type = TYPE_STRING, .func = update_title}},
84 { "insert_mode", {.ptr = (void *)&uzbl.behave.insert_mode, .type = TYPE_INT, .func = NULL}},
85 { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode, .type = TYPE_INT, .func = cmd_always_insert_mode}},
86 { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode, .type = TYPE_INT, .func = NULL}},
87 { "modkey" , {.ptr = (void *)&uzbl.behave.modkey, .type = TYPE_STRING, .func = cmd_modkey}},
88 { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler, .type = TYPE_STRING, .func = NULL}},
89 { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler, .type = TYPE_STRING, .func = NULL}},
90 { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler, .type = TYPE_STRING, .func = NULL}},
91 { "history_handler", {.ptr = (void *)&uzbl.behave.history_handler, .type = TYPE_STRING, .func = NULL}},
92 { "download_handler", {.ptr = (void *)&uzbl.behave.download_handler, .type = TYPE_STRING, .func = NULL}},
93 { "cookie_handler", {.ptr = (void *)&uzbl.behave.cookie_handler, .type = TYPE_STRING, .func = NULL}},
94 { "fifo_dir", {.ptr = (void *)&uzbl.behave.fifo_dir, .type = TYPE_STRING, .func = cmd_fifo_dir}},
95 { "socket_dir", {.ptr = (void *)&uzbl.behave.socket_dir, .type = TYPE_STRING, .func = cmd_socket_dir}},
96 { "http_debug", {.ptr = (void *)&uzbl.behave.http_debug, .type = TYPE_INT, .func = cmd_http_debug}},
97 { "default_font_size", {.ptr = (void *)&uzbl.behave.default_font_size, .type = TYPE_INT, .func = cmd_default_font_size}},
98 { "minimum_font_size", {.ptr = (void *)&uzbl.behave.minimum_font_size, .type = TYPE_INT, .func = cmd_minimum_font_size}},
99 { "shell_cmd", {.ptr = (void *)&uzbl.behave.shell_cmd, .type = TYPE_STRING, .func = NULL}},
100 { "proxy_url", {.ptr = (void *)&uzbl.net.proxy_url, .type = TYPE_STRING, .func = set_proxy_url}},
101 { "max_conns", {.ptr = (void *)&uzbl.net.max_conns, .type = TYPE_INT, .func = cmd_max_conns}},
102 { "max_conns_host", {.ptr = (void *)&uzbl.net.max_conns_host, .type = TYPE_INT, .func = cmd_max_conns_host}},
103 { "useragent", {.ptr = (void *)&uzbl.net.useragent, .type = TYPE_STRING, .func = cmd_useragent}},
104 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
105 }, *n2v_p = var_name_to_ptr;
111 { "SHIFT", GDK_SHIFT_MASK }, // shift
112 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
113 { "CONTROL", GDK_CONTROL_MASK }, // control
114 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
115 { "MOD2", GDK_MOD2_MASK }, // 5th mod
116 { "MOD3", GDK_MOD3_MASK }, // 6th mod
117 { "MOD4", GDK_MOD4_MASK }, // 7th mod
118 { "MOD5", GDK_MOD5_MASK }, // 8th mod
119 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
120 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
121 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
122 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
123 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
124 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
125 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
126 { "META", GDK_META_MASK }, // meta (since 2.10)
131 /* construct a hash from the var_name_to_ptr array for quick access */
133 make_var_to_name_hash() {
134 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
136 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
141 /* commandline arguments (set initial values for the state variables) */
142 static GOptionEntry entries[] =
144 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
145 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
146 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
147 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
148 { NULL, 0, 0, 0, NULL, NULL, NULL }
151 typedef void (*Command)(WebKitWebView*, GArray *argv);
153 /* --- UTILITY FUNCTIONS --- */
159 snprintf(tmp, sizeof(tmp), "%i", val);
160 return g_strdup(tmp);
164 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
167 str_replace (const char* search, const char* replace, const char* string) {
171 buf = g_strsplit (string, search, -1);
172 ret = g_strjoinv (replace, buf);
179 read_file_by_line (gchar *path) {
180 GIOChannel *chan = NULL;
181 gchar *readbuf = NULL;
183 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
186 chan = g_io_channel_new_file(path, "r", NULL);
189 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
190 const gchar* val = g_strdup (readbuf);
191 g_array_append_val (lines, val);
196 g_io_channel_unref (chan);
198 fprintf(stderr, "File '%s' not be read.\n", path);
205 gchar* parseenv (const char* string) {
206 extern char** environ;
207 gchar* newstring = g_strdup (string);
210 while (environ[i] != NULL) {
211 gchar** env = g_strsplit (environ[i], "=", 0);
212 gchar* envname = malloc (strlen (env[0]) + 1);
214 strcat (envname, "$");
215 strcat (envname, env[0]);
217 newstring = str_replace(envname, env[1], newstring);
220 //g_strfreev (env); - This still breaks uzbl, but shouldn't. The mystery thickens...
228 setup_signal(int signr, sigfunc *shandler) {
229 struct sigaction nh, oh;
231 nh.sa_handler = shandler;
232 sigemptyset(&nh.sa_mask);
235 if(sigaction(signr, &nh, &oh) < 0)
243 if (uzbl.behave.fifo_dir)
244 unlink (uzbl.comm.fifo_path);
245 if (uzbl.behave.socket_dir)
246 unlink (uzbl.comm.socket_path);
248 g_free(uzbl.state.executable_path);
249 g_string_free(uzbl.state.keycmd, TRUE);
250 g_hash_table_destroy(uzbl.bindings);
251 g_hash_table_destroy(uzbl.behave.commands);
255 /* --- SIGNAL HANDLER --- */
258 catch_sigterm(int s) {
264 catch_sigint(int s) {
270 /* --- CALLBACKS --- */
273 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
276 (void) navigation_action;
277 (void) policy_decision;
279 const gchar* uri = webkit_network_request_get_uri (request);
280 if (uzbl.state.verbose)
281 printf("New window requested -> %s \n", uri);
282 new_window_load_uri(uri);
287 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
291 if (uzbl.state.selected_url != NULL) {
292 if (uzbl.state.verbose)
293 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
294 new_window_load_uri(uzbl.state.selected_url);
296 if (uzbl.state.verbose)
297 printf("New web view -> %s\n","Nothing to open, exiting");
303 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
306 if (uzbl.behave.download_handler) {
307 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
308 if (uzbl.state.verbose)
309 printf("Download -> %s\n",uri);
310 /* if urls not escaped, we may have to escape and quote uri before this call */
311 run_handler(uzbl.behave.download_handler, uri);
316 /* scroll a bar in a given direction */
318 scroll (GtkAdjustment* bar, GArray *argv) {
322 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
323 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
324 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
327 static void scroll_begin(WebKitWebView* page, GArray *argv) {
328 (void) page; (void) argv;
329 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
332 static void scroll_end(WebKitWebView* page, GArray *argv) {
333 (void) page; (void) argv;
334 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
335 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
338 static void scroll_vert(WebKitWebView* page, GArray *argv) {
340 scroll(uzbl.gui.bar_v, argv);
343 static void scroll_horz(WebKitWebView* page, GArray *argv) {
345 scroll(uzbl.gui.bar_h, argv);
350 if (!uzbl.behave.show_status) {
351 gtk_widget_hide(uzbl.gui.mainbar);
353 gtk_widget_show(uzbl.gui.mainbar);
359 toggle_status_cb (WebKitWebView* page, GArray *argv) {
363 if (uzbl.behave.show_status) {
364 gtk_widget_hide(uzbl.gui.mainbar);
366 gtk_widget_show(uzbl.gui.mainbar);
368 uzbl.behave.show_status = !uzbl.behave.show_status;
373 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
377 //Set selected_url state variable
378 g_free(uzbl.state.selected_url);
379 uzbl.state.selected_url = NULL;
381 uzbl.state.selected_url = g_strdup(link);
387 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
391 if (uzbl.gui.main_title)
392 g_free (uzbl.gui.main_title);
393 uzbl.gui.main_title = g_strdup (title);
398 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
401 uzbl.gui.sbar.load_progress = progress;
406 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
410 if (uzbl.behave.load_finish_handler)
411 run_handler(uzbl.behave.load_finish_handler, "");
415 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
419 if (uzbl.behave.load_start_handler)
420 run_handler(uzbl.behave.load_start_handler, "");
424 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
427 free (uzbl.state.uri);
428 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
429 uzbl.state.uri = g_string_free (newuri, FALSE);
430 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
431 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
434 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
435 if (uzbl.behave.load_commit_handler)
436 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
440 destroy_cb (GtkWidget* widget, gpointer data) {
448 if (uzbl.behave.history_handler) {
450 struct tm * timeinfo;
453 timeinfo = localtime ( &rawtime );
455 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
457 strftime (date, 80, " \"%Y-%m-%d %H:%M:%S\"", timeinfo);
458 >>>>>>> 17160f7... Remove bunches of old code, fix run_handler, free some stuff:uzbl.c
459 run_handler(uzbl.behave.history_handler, date);
464 /* VIEW funcs (little webkit wrappers) */
465 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
467 VIEWFUNC(reload_bypass_cache)
468 VIEWFUNC(stop_loading)
475 /* -- command to callback/function map for things we cannot attach to any signals */
477 static struct {char *name; Command command[2];} cmdlist[] =
478 { /* key function no_split */
479 { "back", {view_go_back, 0} },
480 { "forward", {view_go_forward, 0} },
481 { "scroll_vert", {scroll_vert, 0} },
482 { "scroll_horz", {scroll_horz, 0} },
483 { "scroll_begin", {scroll_begin, 0} },
484 { "scroll_end", {scroll_end, 0} },
485 { "reload", {view_reload, 0}, },
486 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
487 { "stop", {view_stop_loading, 0}, },
488 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
489 { "zoom_out", {view_zoom_out, 0}, },
490 { "uri", {load_uri, NOSPLIT} },
491 { "js", {run_js, NOSPLIT} },
492 { "script", {run_external_js, 0} },
493 { "toggle_status", {toggle_status_cb, 0} },
494 { "spawn", {spawn, 0} },
495 { "sh", {spawn_sh, 0} },
496 { "exit", {close_uzbl, 0} },
497 { "search", {search_forward_text, NOSPLIT} },
498 { "search_reverse", {search_reverse_text, NOSPLIT} },
499 { "toggle_insert_mode", {toggle_insert_mode, 0} },
500 { "runcmd", {runcmd, NOSPLIT} }
507 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
509 for (i = 0; i < LENGTH(cmdlist); i++)
510 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
513 /* -- CORE FUNCTIONS -- */
516 free_action(gpointer act) {
517 Action *action = (Action*)act;
518 g_free(action->name);
520 g_free(action->param);
525 new_action(const gchar *name, const gchar *param) {
526 Action *action = g_new(Action, 1);
528 action->name = g_strdup(name);
530 action->param = g_strdup(param);
532 action->param = NULL;
538 file_exists (const char * filename) {
539 return (access(filename, F_OK) == 0);
543 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);
596 if (uzbl.state.verbose)
597 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
599 if (argv_idx (argv, 1)) {
600 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
604 webkit_web_view_execute_script (web_view, js);
606 g_array_free (lines, TRUE);
611 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
612 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
613 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
615 if (uzbl.state.searchtx != NULL) {
616 if (uzbl.state.verbose)
617 printf ("Searching: %s\n", uzbl.state.searchtx);
619 if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
620 webkit_web_view_unmark_text_matches (page);
621 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
623 if (uzbl.state.searchold != NULL)
624 g_free (uzbl.state.searchold);
626 uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
629 webkit_web_view_set_highlight_text_matches (page, TRUE);
630 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
631 g_free(uzbl.state.searchtx);
632 uzbl.state.searchtx = NULL;
637 search_forward_text (WebKitWebView *page, GArray *argv) {
638 search_text(page, argv, TRUE);
642 search_reverse_text (WebKitWebView *page, GArray *argv) {
643 search_text(page, argv, FALSE);
647 new_window_load_uri (const gchar * uri) {
648 GString* to_execute = g_string_new ("");
649 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
651 for (i = 0; entries[i].long_name != NULL; i++) {
652 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
653 gchar** str = (gchar**)entries[i].arg_data;
655 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
659 if (uzbl.state.verbose)
660 printf("\n%s\n", to_execute->str);
661 g_spawn_command_line_async (to_execute->str, NULL);
662 g_string_free (to_execute, TRUE);
666 close_uzbl (WebKitWebView *page, GArray *argv) {
672 /* --Statusbar functions-- */
674 build_progressbar_ascii(int percent) {
678 GString *bar = g_string_new("");
680 l = (double)percent*((double)width/100.);
681 l = (int)(l+.5)>=(int)l ? l+.5 : l;
683 for(i=0; i<(int)l; i++)
684 g_string_append(bar, "=");
687 g_string_append(bar, "·");
689 return g_string_free(bar, FALSE);
694 const GScannerConfig scan_config = {
697 ) /* cset_skip_characters */,
702 ) /* cset_identifier_first */,
709 ) /* cset_identifier_nth */,
710 ( "" ) /* cpair_comment_single */,
712 TRUE /* case_sensitive */,
714 FALSE /* skip_comment_multi */,
715 FALSE /* skip_comment_single */,
716 FALSE /* scan_comment_multi */,
717 TRUE /* scan_identifier */,
718 TRUE /* scan_identifier_1char */,
719 FALSE /* scan_identifier_NULL */,
720 TRUE /* scan_symbols */,
721 FALSE /* scan_binary */,
722 FALSE /* scan_octal */,
723 FALSE /* scan_float */,
724 FALSE /* scan_hex */,
725 FALSE /* scan_hex_dollar */,
726 FALSE /* scan_string_sq */,
727 FALSE /* scan_string_dq */,
728 TRUE /* numbers_2_int */,
729 FALSE /* int_2_float */,
730 FALSE /* identifier_2_string */,
731 FALSE /* char_2_token */,
732 FALSE /* symbol_2_token */,
733 TRUE /* scope_0_fallback */,
738 uzbl.scan = g_scanner_new(&scan_config);
739 while(symp->symbol_name) {
740 g_scanner_scope_add_symbol(uzbl.scan, 0,
742 GINT_TO_POINTER(symp->symbol_token));
748 expand_template(const char *template) {
749 if(!template) return NULL;
751 GTokenType token = G_TOKEN_NONE;
752 GString *ret = g_string_new("");
756 g_scanner_input_text(uzbl.scan, template, strlen(template));
757 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
758 token = g_scanner_get_next_token(uzbl.scan);
760 if(token == G_TOKEN_SYMBOL) {
761 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
764 buf = uzbl.state.uri?
765 g_markup_printf_escaped("%s", uzbl.state.uri) :
767 g_string_append(ret, buf);
771 buf = itos(uzbl.gui.sbar.load_progress);
772 g_string_append(ret, buf);
775 case SYM_LOADPRGSBAR:
776 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
777 g_string_append(ret, buf);
781 buf = uzbl.gui.main_title?
782 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
784 g_string_append(ret, buf);
787 case SYM_SELECTED_URI:
788 buf = uzbl.state.selected_url?
789 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
791 g_string_append(ret, buf);
795 buf = itos(uzbl.xwin);
797 uzbl.state.instance_name?uzbl.state.instance_name:buf);
801 buf = uzbl.state.keycmd->str?
802 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
804 g_string_append(ret, buf);
809 uzbl.behave.insert_mode?"[I]":"[C]");
813 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
817 buf = itos(WEBKIT_MAJOR_VERSION);
818 g_string_append(ret, buf);
822 buf = itos(WEBKIT_MINOR_VERSION);
823 g_string_append(ret, buf);
827 buf = itos(WEBKIT_MICRO_VERSION);
828 g_string_append(ret, buf);
832 g_string_append(ret, uzbl.state.unameinfo.sysname);
835 g_string_append(ret, uzbl.state.unameinfo.nodename);
838 g_string_append(ret, uzbl.state.unameinfo.release);
841 g_string_append(ret, uzbl.state.unameinfo.version);
844 g_string_append(ret, uzbl.state.unameinfo.machine);
847 g_string_append(ret, ARCH);
851 g_string_append(ret, uzbl.state.unameinfo.domainname);
855 g_string_append(ret, COMMIT);
861 else if(token == G_TOKEN_INT) {
862 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
863 g_string_append(ret, buf);
866 else if(token == G_TOKEN_IDENTIFIER) {
867 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
869 else if(token == G_TOKEN_CHAR) {
870 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
874 return g_string_free(ret, FALSE);
876 /* --End Statusbar functions-- */
879 sharg_append(GArray *a, const gchar *str) {
880 const gchar *s = (str ? str : "");
881 g_array_append_val(a, s);
884 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
886 run_command (const gchar *command, const guint npre, const gchar **args,
887 const gboolean sync, char **stdout) {
888 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
891 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
892 gchar *pid = itos(getpid());
893 gchar *xwin = itos(uzbl.xwin);
895 sharg_append(a, command);
896 for (i = 0; i < npre; i++) /* add n args before the default vars */
897 sharg_append(a, args[i]);
898 sharg_append(a, uzbl.state.config_file);
899 sharg_append(a, pid);
900 sharg_append(a, xwin);
901 sharg_append(a, uzbl.comm.fifo_path);
902 sharg_append(a, uzbl.comm.socket_path);
903 sharg_append(a, uzbl.state.uri);
904 sharg_append(a, uzbl.gui.main_title);
907 for (i = npre; i < g_strv_length((gchar**)args); i++)
910 for (i = 0; i < g_strv_length(args); i++)
911 >>>>>>> 17160f7... Remove bunches of old code, fix run_handler, free some stuff:uzbl.c
912 sharg_append(a, args[i]);
914 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
915 NULL, NULL, stdout, NULL, NULL, &err);
916 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
917 NULL, NULL, NULL, &err);
919 if (uzbl.state.verbose) {
921 GString *s = g_string_new("spawned:");
922 for (i = 0; i < (a->len); i++) {
923 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
924 g_string_append_printf(s, " %s", qarg);
927 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
928 printf("%s\n", s->str);
929 g_string_free(s, TRUE);
931 gchar *cli = g_strjoinv(" ", (gchar **)a->data);
932 printf("Called %s. Result: %s\n", cli, (result ? "TRUE" : "FALSE" ));
934 >>>>>>> 17160f7... Remove bunches of old code, fix run_handler, free some stuff:uzbl.c
937 g_printerr("error on run_command: %s\n", err->message);
943 g_array_free (a, TRUE);
945 g_array_free (a, FALSE);
946 >>>>>>> 17160f7... Remove bunches of old code, fix run_handler, free some stuff:uzbl.c
951 split_quoted(const gchar* src, const gboolean unquote) {
952 /* split on unquoted space, return array of strings;
953 remove a layer of quotes and backslashes if unquote */
954 if (!src) return NULL;
958 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
959 GString *s = g_string_new ("");
963 for (p = src; *p != '\0'; p++) {
964 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
965 else if (*p == '\\') { g_string_append_c(s, *p++);
966 g_string_append_c(s, *p); }
967 else if ((*p == '"') && unquote && !sq) dq = !dq;
968 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
970 else if ((*p == '\'') && unquote && !dq) sq = !sq;
971 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
973 else if ((*p == ' ') && !dq && !sq) {
974 dup = g_strdup(s->str);
975 g_array_append_val(a, dup);
976 g_string_truncate(s, 0);
977 } else g_string_append_c(s, *p);
979 dup = g_strdup(s->str);
980 g_array_append_val(a, dup);
981 ret = (gchar**)a->data;
982 g_array_free (a, FALSE);
983 g_string_free (s, TRUE);
988 spawn(WebKitWebView *web_view, GArray *argv) {
990 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
992 if (argv_idx(argv, 0)) run_command(argv_idx(argv, 0), 0, (const gchar **) argv->data + sizeof(gchar*), FALSE, NULL);
994 gchar **cmd = split_quoted(param);
995 if (cmd) run_command(cmd[0], &cmd[1], FALSE, NULL);
997 >>>>>>> 17160f7... Remove bunches of old code, fix run_handler, free some stuff:uzbl.c
1001 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1003 if (!uzbl.behave.shell_cmd) {
1004 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1009 gchar *spacer = g_strdup("");
1010 g_array_insert_val(argv, 1, spacer);
1011 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1013 for (i = 1; i < g_strv_length(cmd); i++)
1014 g_array_prepend_val(argv, cmd[i]);
1016 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1022 parse_command(const char *cmd, const char *param) {
1025 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1028 gchar **par = split_quoted(param, TRUE);
1029 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1031 if (c[1]) { /* don't split */
1032 sharg_append(a, param);
1034 for (i = 0; i < g_strv_length(par); i++)
1035 sharg_append(a, par[i]);
1037 c[0](uzbl.gui.web_view, a);
1039 g_array_free (a, TRUE);
1042 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1045 /* command parser */
1048 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1049 G_REGEX_OPTIMIZE, 0, NULL);
1050 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1051 G_REGEX_OPTIMIZE, 0, NULL);
1052 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1053 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1054 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1055 G_REGEX_OPTIMIZE, 0, NULL);
1056 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1057 G_REGEX_OPTIMIZE, 0, NULL);
1061 get_var_value(gchar *name) {
1064 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1065 if(c->type == TYPE_STRING)
1066 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1067 else if(c->type == TYPE_INT)
1068 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1077 if(*uzbl.net.proxy_url == ' '
1078 || uzbl.net.proxy_url == NULL) {
1079 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1080 (GType) SOUP_SESSION_PROXY_URI);
1083 suri = soup_uri_new(uzbl.net.proxy_url);
1084 g_object_set(G_OBJECT(uzbl.net.soup_session),
1085 SOUP_SESSION_PROXY_URI,
1087 soup_uri_free(suri);
1094 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1095 g_array_append_val (a, uzbl.state.uri);
1096 load_uri(uzbl.gui.web_view, a);
1097 g_array_free (a, TRUE);
1101 cmd_always_insert_mode() {
1102 uzbl.behave.insert_mode =
1103 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1109 g_object_set(G_OBJECT(uzbl.net.soup_session),
1110 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1114 cmd_max_conns_host() {
1115 g_object_set(G_OBJECT(uzbl.net.soup_session),
1116 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1121 soup_session_remove_feature
1122 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1123 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1124 /*g_free(uzbl.net.soup_logger);*/
1126 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1127 soup_session_add_feature(uzbl.net.soup_session,
1128 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1132 cmd_default_font_size() {
1133 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1134 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1138 cmd_minimum_font_size() {
1139 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1140 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1147 buf = init_fifo(uzbl.behave.fifo_dir);
1148 if(uzbl.behave.fifo_dir)
1149 free(uzbl.behave.fifo_dir);
1151 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1158 buf = init_socket(uzbl.behave.fifo_dir);
1159 if(uzbl.behave.socket_dir)
1160 free(uzbl.behave.socket_dir);
1162 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1170 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1171 uzbl.behave.modmask = 0;
1173 if(uzbl.behave.modkey)
1174 free(uzbl.behave.modkey);
1176 for (i = 0; modkeys[i].key != NULL; i++) {
1177 if (g_strrstr(buf, modkeys[i].key))
1178 uzbl.behave.modmask |= modkeys[i].mask;
1186 buf = set_useragent(uzbl.net.useragent);
1187 if(uzbl.net.useragent)
1188 free(uzbl.net.useragent);
1190 uzbl.net.useragent = buf?buf:g_strdup("");
1195 gtk_widget_ref(uzbl.gui.scrolled_win);
1196 gtk_widget_ref(uzbl.gui.mainbar);
1197 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1198 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1200 if(uzbl.behave.status_top) {
1201 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1202 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.scrolled_win, TRUE, TRUE, 0);
1206 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1208 gtk_widget_unref(uzbl.gui.scrolled_win);
1209 gtk_widget_unref(uzbl.gui.mainbar);
1210 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1215 set_var_value(gchar *name, gchar *val) {
1217 uzbl_cmdprop *c = NULL;
1221 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1222 /* check for the variable type */
1223 if (c->type == TYPE_STRING) {
1225 *c->ptr = g_strdup(val);
1226 } else if(c->type == TYPE_INT) {
1228 *ip = (int)strtoul(val, &endp, 10);
1231 /* invoke a command specific function */
1232 if(c->func) c->func();
1234 /* this will be removed as soon as we have converted to
1235 * the callback interface
1243 runcmd(WebKitWebView* page, GArray *argv) {
1245 parse_cmd_line(argv_idx(argv, 0));
1249 parse_cmd_line(const char *ctl_line) {
1253 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1254 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1255 if(tokens[0][0] == 0) {
1256 gchar* value = parseenv (tokens[2]);
1257 set_var_value(tokens[1], value);
1262 printf("Error in command: %s\n", tokens[0]);
1265 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1266 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1267 if(tokens[0][0] == 0) {
1268 get_var_value(tokens[1]);
1272 printf("Error in command: %s\n", tokens[0]);
1275 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1276 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1277 if(tokens[0][0] == 0) {
1278 gchar* value = parseenv (tokens[2]);
1279 add_binding(tokens[1], value);
1284 printf("Error in command: %s\n", tokens[0]);
1287 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1288 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1289 if(tokens[0][0] == 0) {
1290 parse_command(tokens[1], tokens[2]);
1294 printf("Error in command: %s\n", tokens[0]);
1296 /* KEYCMD command */
1297 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1298 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1299 if(tokens[0][0] == 0) {
1300 /* should incremental commands want each individual "keystroke"
1301 sent in a loop or the whole string in one go like now? */
1302 g_string_assign(uzbl.state.keycmd, tokens[1]);
1304 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1311 else if( (ctl_line[0] == '#')
1312 || (ctl_line[0] == ' ')
1313 || (ctl_line[0] == '\n'))
1314 ; /* ignore these lines */
1316 printf("Command not understood (%s)\n", ctl_line);
1322 build_stream_name(int type, const gchar* dir) {
1324 State *s = &uzbl.state;
1327 xwin_str = itos((int)uzbl.xwin);
1329 str = g_strdup_printf
1330 ("%s/uzbl_fifo_%s", dir,
1331 s->instance_name ? s->instance_name : xwin_str);
1332 } else if (type == SOCKET) {
1333 str = g_strdup_printf
1334 ("%s/uzbl_socket_%s", dir,
1335 s->instance_name ? s->instance_name : xwin_str );
1342 control_fifo(GIOChannel *gio, GIOCondition condition) {
1343 if (uzbl.state.verbose)
1344 printf("triggered\n");
1349 if (condition & G_IO_HUP)
1350 g_error ("Fifo: Read end of pipe died!\n");
1353 g_error ("Fifo: GIOChannel broke\n");
1355 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1356 if (ret == G_IO_STATUS_ERROR) {
1357 g_error ("Fifo: Error reading: %s\n", err->message);
1361 parse_cmd_line(ctl_line);
1368 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1369 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1370 if (unlink(uzbl.comm.fifo_path) == -1)
1371 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1372 g_free(uzbl.comm.fifo_path);
1373 uzbl.comm.fifo_path = NULL;
1376 if (*dir == ' ') { /* space unsets the variable */
1380 GIOChannel *chan = NULL;
1381 GError *error = NULL;
1382 gchar *path = build_stream_name(FIFO, dir);
1384 if (!file_exists(path)) {
1385 if (mkfifo (path, 0666) == 0) {
1386 // 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.
1387 chan = g_io_channel_new_file(path, "r+", &error);
1389 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1390 if (uzbl.state.verbose)
1391 printf ("init_fifo: created successfully as %s\n", path);
1392 uzbl.comm.fifo_path = path;
1394 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1395 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1396 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1397 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1399 /* if we got this far, there was an error; cleanup */
1400 if (error) g_error_free (error);
1406 control_stdin(GIOChannel *gio, GIOCondition condition) {
1407 gchar *ctl_line = NULL;
1410 if (condition & G_IO_HUP) {
1411 ret = g_io_channel_shutdown (gio, FALSE, NULL);
1415 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1416 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1419 parse_cmd_line(ctl_line);
1427 GIOChannel *chan = NULL;
1428 GError *error = NULL;
1430 chan = g_io_channel_unix_new(fileno(stdin));
1432 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1433 g_error ("Stdin: could not add watch\n");
1435 if (uzbl.state.verbose)
1436 printf ("Stdin: watch added successfully\n");
1439 g_error ("Stdin: Error while opening: %s\n", error->message);
1441 if (error) g_error_free (error);
1445 control_socket(GIOChannel *chan) {
1446 struct sockaddr_un remote;
1447 char buffer[512], *ctl_line;
1449 int sock, clientsock, n, done;
1452 sock = g_io_channel_unix_get_fd(chan);
1454 memset (buffer, 0, sizeof (buffer));
1456 t = sizeof (remote);
1457 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1461 memset (temp, 0, sizeof (temp));
1462 n = recv (clientsock, temp, 128, 0);
1464 buffer[strlen (buffer)] = '\0';
1468 strcat (buffer, temp);
1471 if (strcmp (buffer, "\n") < 0) {
1472 buffer[strlen (buffer) - 1] = '\0';
1474 buffer[strlen (buffer)] = '\0';
1477 ctl_line = g_strdup(buffer);
1478 parse_cmd_line (ctl_line);
1481 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1482 GError *error = NULL;
1485 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1486 if (ret == G_IO_STATUS_ERROR)
1487 g_error ("Error reading: %s\n", error->message);
1489 printf("Got line %s (%u bytes) \n",ctl_line, len);
1491 parse_line(ctl_line);
1499 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1500 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1501 if (unlink(uzbl.comm.socket_path) == -1)
1502 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1503 g_free(uzbl.comm.socket_path);
1504 uzbl.comm.socket_path = NULL;
1512 GIOChannel *chan = NULL;
1514 struct sockaddr_un local;
1515 gchar *path = build_stream_name(SOCKET, dir);
1517 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1519 local.sun_family = AF_UNIX;
1520 strcpy (local.sun_path, path);
1521 unlink (local.sun_path);
1523 len = strlen (local.sun_path) + sizeof (local.sun_family);
1524 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1525 if (uzbl.state.verbose)
1526 printf ("init_socket: opened in %s\n", path);
1529 if( (chan = g_io_channel_unix_new(sock)) ) {
1530 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1531 uzbl.comm.socket_path = path;
1534 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1536 /* if we got this far, there was an error; cleanup */
1543 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1544 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1546 // this function may be called very early when the templates are not set (yet), hence the checks
1548 update_title (void) {
1549 Behaviour *b = &uzbl.behave;
1552 if (b->show_status) {
1553 if (b->title_format_short) {
1554 parsed = expand_template(b->title_format_short);
1555 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1558 if (b->status_format) {
1559 parsed = expand_template(b->status_format);
1560 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1563 if (b->status_background) {
1565 gdk_color_parse (b->status_background, &color);
1566 //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)
1567 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1570 if (b->title_format_long) {
1571 parsed = expand_template(b->title_format_long);
1572 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1579 key_press_cb (GtkWidget* window, GdkEventKey* event)
1581 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1585 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1586 || 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)
1589 /* turn off insert mode (if always_insert_mode is not used) */
1590 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1591 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1596 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1599 if (event->keyval == GDK_Escape) {
1600 g_string_truncate(uzbl.state.keycmd, 0);
1605 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1606 if (event->keyval == GDK_Insert) {
1608 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1609 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1611 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1614 g_string_append (uzbl.state.keycmd, str);
1621 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1622 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1626 gboolean key_ret = FALSE;
1627 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1629 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1631 run_keycmd(key_ret);
1633 if (key_ret) return (!uzbl.behave.insert_mode);
1638 run_keycmd(const gboolean key_ret) {
1639 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1641 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1642 g_string_truncate(uzbl.state.keycmd, 0);
1643 parse_command(action->name, action->param);
1647 /* try if it's an incremental keycmd or one that takes args, and run it */
1648 GString* short_keys = g_string_new ("");
1649 GString* short_keys_inc = g_string_new ("");
1651 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1652 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1653 g_string_assign(short_keys_inc, short_keys->str);
1654 g_string_append_c(short_keys, '_');
1655 g_string_append_c(short_keys_inc, '*');
1657 gboolean exec_now = FALSE;
1658 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1659 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1660 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1661 if (key_ret) { /* just quit the incremental command on return */
1662 g_string_truncate(uzbl.state.keycmd, 0);
1664 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1668 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1669 GString* actionname = g_string_new ("");
1670 GString* actionparam = g_string_new ("");
1671 g_string_erase (parampart, 0, i+1);
1673 g_string_printf (actionname, action->name, parampart->str);
1675 g_string_printf (actionparam, action->param, parampart->str);
1676 parse_command(actionname->str, actionparam->str);
1677 g_string_free (actionname, TRUE);
1678 g_string_free (actionparam, TRUE);
1679 g_string_free (parampart, TRUE);
1681 g_string_truncate(uzbl.state.keycmd, 0);
1685 g_string_truncate(short_keys, short_keys->len - 1);
1687 g_string_free (short_keys, TRUE);
1688 g_string_free (short_keys_inc, TRUE);
1695 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1696 //main_window_ref = g_object_ref(scrolled_window);
1697 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
1699 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1700 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1702 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1703 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1704 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1705 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1706 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1707 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1708 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1709 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1710 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1711 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1713 return scrolled_window;
1720 g->mainbar = gtk_hbox_new (FALSE, 0);
1722 /* keep a reference to the bar so we can re-pack it at runtime*/
1723 //sbar_ref = g_object_ref(g->mainbar);
1725 g->mainbar_label = gtk_label_new ("");
1726 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1727 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1728 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1729 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1730 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1735 GtkWidget* create_window () {
1736 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1737 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1738 gtk_widget_set_name (window, "Uzbl browser");
1739 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1740 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1746 run_handler (const gchar *act, const gchar *args) {
1747 char **parts = g_strsplit(act, " ", 2);
1750 else if ((g_strcmp0(parts[0], "spawn") == 0)
1751 || (g_strcmp0(parts[0], "sh") == 0)) {
1753 GString *a = g_string_new ("");
1755 spawnparts = split_quoted(parts[1], FALSE);
1756 g_string_append_printf(a, "%s", spawnparts[0]);
1757 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1758 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1759 g_string_append_printf(a, " %s", spawnparts[i]);
1761 else if (g_strcmp0(parts[0], "spawn") == 0) {
1762 GString *a = g_string_new ("");
1764 spawnparts = split_quoted(parts[1]);
1765 g_string_append_printf(a, "\"%s\"", spawnparts[0]);
1766 g_string_append(a, args); /* append handler args before user args */
1768 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1769 g_string_append_printf(a, " \"%s\"", spawnparts[i]);
1770 >>>>>>> 17160f7... Remove bunches of old code, fix run_handler, free some stuff:uzbl.c
1771 parse_command(parts[0], a->str);
1772 g_string_free (a, TRUE);
1773 g_strfreev (spawnparts);
1775 parse_command(parts[0], parts[1]);
1780 add_binding (const gchar *key, const gchar *act) {
1781 char **parts = g_strsplit(act, " ", 2);
1788 if (uzbl.state.verbose)
1789 printf ("Binding %-10s : %s\n", key, act);
1790 action = new_action(parts[0], parts[1]);
1792 if(g_hash_table_lookup(uzbl.bindings, key))
1793 g_hash_table_remove(uzbl.bindings, key);
1794 g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1800 get_xdg_var (XDG_Var xdg) {
1801 const gchar* actual_value = getenv (xdg.environmental);
1802 const gchar* home = getenv ("HOME");
1804 gchar* return_value = str_replace ("~", home, actual_value);
1806 if (! actual_value || strcmp (actual_value, "") == 0) {
1807 if (xdg.default_value) {
1808 return_value = str_replace ("~", home, xdg.default_value);
1810 return_value = NULL;
1813 return return_value;
1817 find_xdg_file (int xdg_type, char* filename) {
1818 /* xdg_type = 0 => config
1819 xdg_type = 1 => data
1820 xdg_type = 2 => cache*/
1822 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1823 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1826 gchar* temporary_string;
1830 if (! file_exists (temporary_file) && xdg_type != 2) {
1831 buf = get_xdg_var (XDG[3 + xdg_type]);
1832 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1835 while (temporary_string && ! file_exists (temporary_file)) {
1836 strcpy (temporary_file, temporary_string);
1837 strcat (temporary_file, filename);
1838 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1842 g_free (temporary_string);
1844 if (file_exists (temporary_file)) {
1845 return temporary_file;
1853 State *s = &uzbl.state;
1854 Network *n = &uzbl.net;
1856 uzbl.behave.reset_command_mode = 1;
1858 if (!s->config_file) {
1859 s->config_file = find_xdg_file (0, "/uzbl/config");
1862 if (s->config_file) {
1863 GIOChannel *chan = NULL;
1864 gchar *readbuf = NULL;
1867 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1870 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1871 == G_IO_STATUS_NORMAL) {
1872 parse_cmd_line(readbuf);
1876 g_io_channel_unref (chan);
1877 if (uzbl.state.verbose)
1878 printf ("Config %s loaded\n", s->config_file);
1880 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1883 if (uzbl.state.verbose)
1884 printf ("No configuration file loaded.\n");
1886 if (!uzbl.behave.status_format)
1887 set_var_value("status_format", STATUS_DEFAULT);
1888 if (!uzbl.behave.title_format_long)
1889 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1890 if (!uzbl.behave.title_format_short)
1891 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1894 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1898 set_useragent(gchar *val) {
1903 gchar *ua = expand_template(val);
1905 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1909 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1912 if (!uzbl.behave.cookie_handler) return;
1914 gchar * stdout = NULL;
1915 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1916 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1917 gchar *action = g_strdup ("GET");
1918 SoupURI * soup_uri = soup_message_get_uri(msg);
1919 sharg_append(a, action);
1920 sharg_append(a, soup_uri->host);
1921 sharg_append(a, soup_uri->path);
1922 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1923 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1925 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1928 g_array_free(a, TRUE);
1932 save_cookies (SoupMessage *msg, gpointer user_data){
1936 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1937 cookie = soup_cookie_to_set_cookie_header(ck->data);
1938 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1939 SoupURI * soup_uri = soup_message_get_uri(msg);
1940 gchar *action = strdup("PUT");
1941 sharg_append(a, action);
1942 sharg_append(a, soup_uri->host);
1943 sharg_append(a, soup_uri->path);
1944 sharg_append(a, cookie);
1945 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1948 g_array_free(a, TRUE);
1954 main (int argc, char* argv[]) {
1955 gtk_init (&argc, &argv);
1956 if (!g_thread_supported ())
1957 g_thread_init (NULL);
1959 uzbl.state.executable_path = g_strdup(argv[0]);
1960 uzbl.state.selected_url = NULL;
1961 uzbl.state.searchtx = NULL;
1963 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1964 g_option_context_add_main_entries (context, entries, NULL);
1965 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1966 g_option_context_parse (context, &argc, &argv, NULL);
1967 /* initialize hash table */
1968 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1970 uzbl.net.soup_session = webkit_get_default_session();
1971 uzbl.state.keycmd = g_string_new("");
1973 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1974 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1975 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1976 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1978 if(uname(&uzbl.state.unameinfo) == -1)
1979 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1984 make_var_to_name_hash();
1986 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1988 uzbl.gui.scrolled_win = create_browser();
1991 /* initial packing */
1992 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1993 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1995 uzbl.gui.main_window = create_window ();
1996 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1998 //load_uri (uzbl.gui.web_view, uzbl.state.uri); //TODO: is this needed?
2000 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2001 gtk_widget_show_all (uzbl.gui.main_window);
2002 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2004 if (uzbl.state.verbose) {
2005 printf("Uzbl start location: %s\n", argv[0]);
2006 printf("window_id %i\n",(int) uzbl.xwin);
2007 printf("pid %i\n", getpid ());
2008 printf("name: %s\n", uzbl.state.instance_name);
2011 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2012 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2013 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2014 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2015 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2019 if (!uzbl.behave.show_status)
2020 gtk_widget_hide(uzbl.gui.mainbar);
2026 //if(uzbl.state.uri)
2027 // load_uri (uzbl.gui.web_view, uzbl.state.uri);
2032 return EXIT_SUCCESS;
2035 /* vi: set et ts=4: */