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 */
65 } var_name_to_ptr[] = {
66 { "uri", (void *)&uzbl.state.uri },
67 { "status_message", (void *)&uzbl.gui.sbar.msg },
68 { "show_status", (void *)&uzbl.behave.show_status },
69 { "status_top", (void *)&uzbl.behave.status_top },
70 { "status_format", (void *)&uzbl.behave.status_format },
71 { "status_background", (void *)&uzbl.behave.status_background },
72 { "title_format_long", (void *)&uzbl.behave.title_format_long },
73 { "title_format_short", (void *)&uzbl.behave.title_format_short },
74 { "insert_mode", (void *)&uzbl.behave.insert_mode },
75 { "always_insert_mode", (void *)&uzbl.behave.always_insert_mode },
76 { "reset_command_mode", (void *)&uzbl.behave.reset_command_mode },
77 { "modkey" , (void *)&uzbl.behave.modkey },
78 { "load_finish_handler",(void *)&uzbl.behave.load_finish_handler},
79 { "history_handler", (void *)&uzbl.behave.history_handler },
80 { "download_handler", (void *)&uzbl.behave.download_handler },
81 { "cookie_handler", (void *)&uzbl.behave.cookie_handler },
82 { "fifo_dir", (void *)&uzbl.behave.fifo_dir },
83 { "socket_dir", (void *)&uzbl.behave.socket_dir },
84 { "http_debug", (void *)&uzbl.behave.http_debug },
85 { "default_font_size", (void *)&uzbl.behave.default_font_size },
86 { "minimum_font_size", (void *)&uzbl.behave.minimum_font_size },
87 { "shell_cmd", (void *)&uzbl.behave.shell_cmd },
88 { "proxy_url", (void *)&uzbl.net.proxy_url },
89 { "max_conns", (void *)&uzbl.net.max_conns },
90 { "max_conns_host", (void *)&uzbl.net.max_conns_host },
91 { "useragent", (void *)&uzbl.net.useragent },
93 }, *n2v_p = var_name_to_ptr;
99 { "SHIFT", GDK_SHIFT_MASK }, // shift
100 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
101 { "CONTROL", GDK_CONTROL_MASK }, // control
102 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
103 { "MOD2", GDK_MOD2_MASK }, // 5th mod
104 { "MOD3", GDK_MOD3_MASK }, // 6th mod
105 { "MOD4", GDK_MOD4_MASK }, // 7th mod
106 { "MOD5", GDK_MOD5_MASK }, // 8th mod
107 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
108 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
109 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
110 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
111 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
112 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
113 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
114 { "META", GDK_META_MASK }, // meta (since 2.10)
118 /* construct a hash from the var_name_to_ptr array for quick access */
120 make_var_to_name_hash() {
121 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
123 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, n2v_p->ptr);
128 /* commandline arguments (set initial values for the state variables) */
129 static GOptionEntry entries[] =
131 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
132 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
133 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
134 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
135 { NULL, 0, 0, 0, NULL, NULL, NULL }
138 typedef void (*Command)(WebKitWebView*, const char *);
140 /* --- UTILITY FUNCTIONS --- */
146 snprintf(tmp, sizeof(tmp), "%i", val);
147 return g_strdup(tmp);
151 str_replace (const char* search, const char* replace, const char* string) {
155 buf = g_strsplit (string, search, -1);
156 ret = g_strjoinv (replace, buf);
163 setup_signal(int signr, sigfunc *shandler) {
164 struct sigaction nh, oh;
166 nh.sa_handler = shandler;
167 sigemptyset(&nh.sa_mask);
170 if(sigaction(signr, &nh, &oh) < 0)
178 if (uzbl.behave.fifo_dir)
179 unlink (uzbl.comm.fifo_path);
180 if (uzbl.behave.socket_dir)
181 unlink (uzbl.comm.socket_path);
183 g_string_free(uzbl.state.keycmd, TRUE);
184 g_hash_table_destroy(uzbl.bindings);
185 g_hash_table_destroy(uzbl.behave.commands);
189 /* --- SIGNAL HANDLER --- */
192 catch_sigterm(int s) {
198 catch_sigint(int s) {
204 /* --- CALLBACKS --- */
207 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
210 (void) navigation_action;
211 (void) policy_decision;
213 const gchar* uri = webkit_network_request_get_uri (request);
214 if (uzbl.state.verbose)
215 printf("New window requested -> %s \n", uri);
216 new_window_load_uri(uri);
221 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
225 if (uzbl.state.selected_url[0]!=0) {
226 if (uzbl.state.verbose)
227 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
228 new_window_load_uri(uzbl.state.selected_url);
230 if (uzbl.state.verbose)
231 printf("New web view -> %s\n","Nothing to open, exiting");
237 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
240 if (uzbl.behave.download_handler) {
241 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
242 if (uzbl.state.verbose)
243 printf("Download -> %s\n",uri);
244 run_command(uzbl.behave.download_handler, uri, FALSE, NULL);
249 /* scroll a bar in a given direction */
251 scroll (GtkAdjustment* bar, const char *param) {
255 amount = g_ascii_strtod(param, &end);
258 fprintf(stderr, "found something after double: %s\n", end);
260 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
263 static void scroll_begin(WebKitWebView* page, const char *param) {
264 (void) page; (void) param;
265 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
268 static void scroll_end(WebKitWebView* page, const char *param) {
269 (void) page; (void) param;
270 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
271 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
274 static void scroll_vert(WebKitWebView* page, const char *param) {
276 scroll(uzbl.gui.bar_v, param);
279 static void scroll_horz(WebKitWebView* page, const char *param) {
281 scroll(uzbl.gui.bar_h, param);
286 if (!uzbl.behave.show_status) {
287 gtk_widget_hide(uzbl.gui.mainbar);
289 gtk_widget_show(uzbl.gui.mainbar);
295 toggle_status_cb (WebKitWebView* page, const char *param) {
299 if (uzbl.behave.show_status) {
300 gtk_widget_hide(uzbl.gui.mainbar);
302 gtk_widget_show(uzbl.gui.mainbar);
304 uzbl.behave.show_status = !uzbl.behave.show_status;
309 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
313 //Set selected_url state variable
314 uzbl.state.selected_url[0] = '\0';
316 strcpy (uzbl.state.selected_url, link);
322 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
326 if (uzbl.gui.main_title)
327 g_free (uzbl.gui.main_title);
328 uzbl.gui.main_title = g_strdup (title);
333 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
336 uzbl.gui.sbar.load_progress = progress;
341 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
345 if (uzbl.behave.load_finish_handler) {
346 run_command(uzbl.behave.load_finish_handler, NULL, FALSE, NULL);
351 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
354 free (uzbl.state.uri);
355 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
356 uzbl.state.uri = g_string_free (newuri, FALSE);
357 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
358 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
361 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
365 destroy_cb (GtkWidget* widget, gpointer data) {
373 if (uzbl.behave.history_handler) {
375 struct tm * timeinfo;
378 timeinfo = localtime ( &rawtime );
379 strftime (date, 80, "%Y-%m-%d %H:%M:%S", timeinfo);
380 GString* args = g_string_new ("");
381 g_string_printf (args, "'%s'", date);
382 run_command(uzbl.behave.history_handler, args->str, FALSE, NULL);
383 g_string_free (args, TRUE);
388 /* VIEW funcs (little webkit wrappers) */
389 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
391 VIEWFUNC(reload_bypass_cache)
392 VIEWFUNC(stop_loading)
399 /* -- command to callback/function map for things we cannot attach to any signals */
402 static struct {char *name; Command command;} cmdlist[] =
404 { "back", view_go_back },
405 { "forward", view_go_forward },
406 { "scroll_vert", scroll_vert },
407 { "scroll_horz", scroll_horz },
408 { "scroll_begin", scroll_begin },
409 { "scroll_end", scroll_end },
410 { "reload", view_reload, },
411 { "reload_ign_cache", view_reload_bypass_cache},
412 { "stop", view_stop_loading, },
413 { "zoom_in", view_zoom_in, }, //Can crash (when max zoom reached?).
414 { "zoom_out", view_zoom_out, },
416 { "script", run_js },
417 { "toggle_status", toggle_status_cb },
420 { "exit", close_uzbl },
421 { "search", search_forward_text },
422 { "search_reverse", search_reverse_text },
423 { "insert_mode", set_insert_mode },
431 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
433 for (i = 0; i < LENGTH(cmdlist); i++)
434 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
437 /* -- CORE FUNCTIONS -- */
440 free_action(gpointer act) {
441 Action *action = (Action*)act;
442 g_free(action->name);
444 g_free(action->param);
449 new_action(const gchar *name, const gchar *param) {
450 Action *action = g_new(Action, 1);
452 action->name = g_strdup(name);
454 action->param = g_strdup(param);
456 action->param = NULL;
462 file_exists (const char * filename) {
463 return (access(filename, F_OK) == 0);
467 set_insert_mode(WebKitWebView *page, const gchar *param) {
471 uzbl.behave.insert_mode = TRUE;
476 load_uri (WebKitWebView * web_view, const gchar *param) {
478 GString* newuri = g_string_new (param);
479 if (g_strrstr (param, "://") == NULL)
480 g_string_prepend (newuri, "http://");
481 /* if we do handle cookies, ask our handler for them */
482 webkit_web_view_load_uri (web_view, newuri->str);
483 g_string_free (newuri, TRUE);
488 run_js (WebKitWebView * web_view, const gchar *param) {
490 webkit_web_view_execute_script (web_view, param);
494 search_text (WebKitWebView *page, const char *param, const gboolean forward) {
495 if ((param) && (param[0] != '\0')) {
496 strcpy(uzbl.state.searchtx, param);
498 if (uzbl.state.searchtx[0] != '\0') {
499 if (uzbl.state.verbose)
500 printf ("Searching: %s\n", uzbl.state.searchtx);
501 webkit_web_view_unmark_text_matches (page);
502 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
503 webkit_web_view_set_highlight_text_matches (page, TRUE);
504 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
509 search_forward_text (WebKitWebView *page, const char *param) {
510 search_text(page, param, TRUE);
514 search_reverse_text (WebKitWebView *page, const char *param) {
515 search_text(page, param, FALSE);
519 new_window_load_uri (const gchar * uri) {
520 GString* to_execute = g_string_new ("");
521 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
523 for (i = 0; entries[i].long_name != NULL; i++) {
524 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
525 gchar** str = (gchar**)entries[i].arg_data;
527 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
531 if (uzbl.state.verbose)
532 printf("\n%s\n", to_execute->str);
533 g_spawn_command_line_async (to_execute->str, NULL);
534 g_string_free (to_execute, TRUE);
538 close_uzbl (WebKitWebView *page, const char *param) {
544 /* --Statusbar functions-- */
546 build_progressbar_ascii(int percent) {
550 GString *bar = g_string_new("");
552 l = (double)percent*((double)width/100.);
553 l = (int)(l+.5)>=(int)l ? l+.5 : l;
555 for(i=0; i<(int)l; i++)
556 g_string_append(bar, "=");
559 g_string_append(bar, "·");
561 return g_string_free(bar, FALSE);
566 const GScannerConfig scan_config = {
569 ) /* cset_skip_characters */,
574 ) /* cset_identifier_first */,
581 ) /* cset_identifier_nth */,
582 ( "" ) /* cpair_comment_single */,
584 TRUE /* case_sensitive */,
586 FALSE /* skip_comment_multi */,
587 FALSE /* skip_comment_single */,
588 FALSE /* scan_comment_multi */,
589 TRUE /* scan_identifier */,
590 TRUE /* scan_identifier_1char */,
591 FALSE /* scan_identifier_NULL */,
592 TRUE /* scan_symbols */,
593 FALSE /* scan_binary */,
594 FALSE /* scan_octal */,
595 FALSE /* scan_float */,
596 FALSE /* scan_hex */,
597 FALSE /* scan_hex_dollar */,
598 FALSE /* scan_string_sq */,
599 FALSE /* scan_string_dq */,
600 TRUE /* numbers_2_int */,
601 FALSE /* int_2_float */,
602 FALSE /* identifier_2_string */,
603 FALSE /* char_2_token */,
604 FALSE /* symbol_2_token */,
605 TRUE /* scope_0_fallback */,
610 uzbl.scan = g_scanner_new(&scan_config);
611 while(symp->symbol_name) {
612 g_scanner_scope_add_symbol(uzbl.scan, 0,
614 GINT_TO_POINTER(symp->symbol_token));
620 expand_template(const char *template) {
621 if(!template) return NULL;
623 GTokenType token = G_TOKEN_NONE;
624 GString *ret = g_string_new("");
628 g_scanner_input_text(uzbl.scan, template, strlen(template));
629 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
630 token = g_scanner_get_next_token(uzbl.scan);
632 if(token == G_TOKEN_SYMBOL) {
633 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
636 buf = uzbl.state.uri?
637 g_markup_printf_escaped("%s", uzbl.state.uri) :
639 g_string_append(ret, buf);
643 buf = itos(uzbl.gui.sbar.load_progress);
644 g_string_append(ret, buf);
647 case SYM_LOADPRGSBAR:
648 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
649 g_string_append(ret, buf);
653 buf = uzbl.gui.main_title?
654 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
656 g_string_append(ret, buf);
659 case SYM_SELECTED_URI:
660 buf = uzbl.state.selected_url?
661 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
663 g_string_append(ret, buf);
667 buf = itos(uzbl.xwin);
669 uzbl.state.instance_name?uzbl.state.instance_name:buf);
673 buf = uzbl.state.keycmd->str?
674 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
676 g_string_append(ret, buf);
681 uzbl.behave.insert_mode?"[I]":"[C]");
685 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
689 buf = itos(WEBKIT_MAJOR_VERSION);
690 g_string_append(ret, buf);
694 buf = itos(WEBKIT_MINOR_VERSION);
695 g_string_append(ret, buf);
699 buf = itos(WEBKIT_MICRO_VERSION);
700 g_string_append(ret, buf);
704 g_string_append(ret, uzbl.state.unameinfo.sysname);
707 g_string_append(ret, uzbl.state.unameinfo.nodename);
710 g_string_append(ret, uzbl.state.unameinfo.release);
713 g_string_append(ret, uzbl.state.unameinfo.version);
716 g_string_append(ret, uzbl.state.unameinfo.machine);
719 g_string_append(ret, ARCH);
723 g_string_append(ret, uzbl.state.unameinfo.domainname);
727 g_string_append(ret, COMMIT);
733 else if(token == G_TOKEN_INT) {
734 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
735 g_string_append(ret, buf);
738 else if(token == G_TOKEN_IDENTIFIER) {
739 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
741 else if(token == G_TOKEN_CHAR) {
742 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
746 return g_string_free(ret, FALSE);
748 /* --End Statusbar functions-- */
751 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
753 run_command (const char *command, const char *args, const gboolean sync, char **stdout) {
754 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
755 GString *to_execute = g_string_new ("");
757 gchar *cmd = g_strstrip(g_strdup(command));
758 gchar *qcfg = (uzbl.state.config_file ? g_shell_quote(uzbl.state.config_file) : g_strdup("''"));
759 gchar *qfifo = (uzbl.comm.fifo_path ? g_shell_quote(uzbl.comm.fifo_path) : g_strdup("''"));
760 gchar *qsock = (uzbl.comm.socket_path ? g_shell_quote(uzbl.comm.socket_path) : g_strdup("''"));
761 gchar *quri = (uzbl.state.uri ? g_shell_quote(uzbl.state.uri) : g_strdup("''"));
762 gchar *qtitle = (uzbl.gui.main_title ? g_shell_quote(uzbl.gui.main_title) : g_strdup("''"));
765 g_string_printf (to_execute, "%s %s '%i' '%i' %s %s",
766 cmd, qcfg, (int) getpid(), (int) uzbl.xwin, qfifo, qsock);
767 g_string_append_printf (to_execute, " %s %s", quri, qtitle);
768 if(args) g_string_append_printf (to_execute, " %s", args);
771 result = g_spawn_command_line_sync (to_execute->str, stdout, NULL, NULL, &err);
772 } else result = g_spawn_command_line_async (to_execute->str, &err);
773 if (uzbl.state.verbose)
774 printf("Called %s. Result: %s\n", to_execute->str, (result ? "TRUE" : "FALSE" ));
775 g_string_free (to_execute, TRUE);
777 g_printerr("error on run_command: %s\n", err->message);
791 spawn(WebKitWebView *web_view, const char *param) {
794 TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
795 gchar** cmd = g_strsplit(param, " ", 2);
798 args = g_shell_quote(cmd[1]);
801 run_command(cmd[0], args, FALSE, NULL);
807 run_command(param, NULL, FALSE, NULL);
811 spawn_sh(WebKitWebView *web_view, const char *param) {
813 gchar *cmd = g_strdup_printf(uzbl.behave.shell_cmd, param);
819 parse_command(const char *cmd, const char *param) {
822 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd)))
823 c(uzbl.gui.web_view, param);
825 fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
831 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
832 G_REGEX_OPTIMIZE, 0, NULL);
833 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
834 G_REGEX_OPTIMIZE, 0, NULL);
835 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
836 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
837 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
838 G_REGEX_OPTIMIZE, 0, NULL);
839 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
840 G_REGEX_OPTIMIZE, 0, NULL);
844 get_var_value(gchar *name) {
847 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
848 if(var_is("uri", name)
849 || var_is("status_message", name)
850 || var_is("status_format", name)
851 || var_is("status_background", name)
852 || var_is("title_format_short", name)
853 || var_is("title_format_long", name)
854 || var_is("modkey", name)
855 || var_is("load_finish_handler", name)
856 || var_is("history_handler", name)
857 || var_is("download_handler", name)
858 || var_is("cookie_handler", name)
859 || var_is("fifo_dir", name)
860 || var_is("socket_dir", name)
861 || var_is("shell_cmd", name)
862 || var_is("proxy_url", name)
863 || var_is("useragent", name))
865 printf("VAR: %s VALUE: %s\n", name, (char *)*p);
866 } else printf("VAR: %s VALUE: %d\n", name, (int)*p);
875 if(*uzbl.net.proxy_url == ' '
876 || uzbl.net.proxy_url == NULL) {
877 soup_session_remove_feature_by_type(uzbl.net.soup_session,
878 (GType) SOUP_SESSION_PROXY_URI);
881 suri = soup_uri_new(uzbl.net.proxy_url);
882 g_object_set(G_OBJECT(uzbl.net.soup_session),
883 SOUP_SESSION_PROXY_URI,
893 gtk_widget_ref(uzbl.gui.scrolled_win);
894 gtk_widget_ref(uzbl.gui.mainbar);
895 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
896 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
898 if(uzbl.behave.status_top) {
899 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
900 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
903 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
904 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
906 gtk_widget_unref(uzbl.gui.scrolled_win);
907 gtk_widget_unref(uzbl.gui.mainbar);
908 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
912 var_is(const char *x, const char *y) {
913 return (strcmp(x, y) == 0 ? TRUE : FALSE );
917 set_var_value(gchar *name, gchar *val) {
922 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
923 if(var_is("status_message", name)
924 || var_is("status_background", name)
925 || var_is("status_format", name)
926 || var_is("title_format_long", name)
927 || var_is("title_format_short", name)
928 || var_is("load_finish_handler", name)
929 || var_is("history_handler", name)
930 || var_is("download_handler", name)
931 || var_is("cookie_handler", name)) {
937 else if(var_is("uri", name)) {
940 load_uri(uzbl.gui.web_view, (const gchar*)*p);
942 else if(var_is("proxy_url", name)) {
947 else if(var_is("fifo_dir", name)) {
949 buf = init_fifo(val);
950 *p = buf?buf:g_strdup("");
952 else if(var_is("socket_dir", name)) {
954 buf = init_socket(val);
955 *p = buf?buf:g_strdup("");
957 else if(var_is("modkey", name)) {
960 *p = g_utf8_strup(val, -1);
961 uzbl.behave.modmask = 0;
962 for (i = 0; modkeys[i].key != NULL; i++) {
963 if (g_strrstr(*p, modkeys[i].key))
964 uzbl.behave.modmask |= modkeys[i].mask;
967 else if(var_is("useragent", name)) {
969 buf = set_useragent(val);
970 *p = buf?buf:g_strdup("");
972 else if(var_is("shell_cmd", name)) {
976 /* variables that take int values */
979 *ip = (int)strtoul(val, &endp, 10);
981 if(var_is("show_status", name)) {
984 else if(var_is("always_insert_mode", name)) {
985 uzbl.behave.insert_mode =
986 uzbl.behave.always_insert_mode ? TRUE : FALSE;
989 else if (var_is("max_conns", name)) {
990 g_object_set(G_OBJECT(uzbl.net.soup_session),
991 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
993 else if (var_is("max_conns_host", name)) {
994 g_object_set(G_OBJECT(uzbl.net.soup_session),
995 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
997 else if (var_is("http_debug", name)) {
998 soup_session_remove_feature
999 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1000 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1001 /*g_free(uzbl.net.soup_logger);*/
1003 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1004 soup_session_add_feature(uzbl.net.soup_session,
1005 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1007 else if (var_is("status_top", name)) {
1010 else if (var_is("default_font_size", name)) {
1011 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1012 g_object_set (G_OBJECT(ws), "default-font-size", *ip, NULL);
1014 else if (var_is("minimum_font_size", name)) {
1015 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1016 g_object_set (G_OBJECT(ws), "minimum-font-size", *ip, NULL);
1024 runcmd(WebKitWebView* page, const char *param) {
1026 parse_cmd_line(param);
1030 parse_cmd_line(const char *ctl_line) {
1034 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1035 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1036 if(tokens[0][0] == 0) {
1037 set_var_value(tokens[1], tokens[2]);
1041 printf("Error in command: %s\n", tokens[0]);
1044 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1045 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1046 if(tokens[0][0] == 0) {
1047 get_var_value(tokens[1]);
1051 printf("Error in command: %s\n", tokens[0]);
1054 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1055 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1056 if(tokens[0][0] == 0) {
1057 add_binding(tokens[1], tokens[2]);
1061 printf("Error in command: %s\n", tokens[0]);
1064 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1065 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1066 if(tokens[0][0] == 0) {
1067 parse_command(tokens[1], tokens[2]);
1071 printf("Error in command: %s\n", tokens[0]);
1073 /* KEYCMD command */
1074 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1075 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1076 if(tokens[0][0] == 0) {
1077 /* should incremental commands want each individual "keystroke"
1078 sent in a loop or the whole string in one go like now? */
1079 g_string_assign(uzbl.state.keycmd, tokens[1]);
1086 else if( (ctl_line[0] == '#')
1087 || (ctl_line[0] == ' ')
1088 || (ctl_line[0] == '\n'))
1089 ; /* ignore these lines */
1091 printf("Command not understood (%s)\n", ctl_line);
1097 build_stream_name(int type, const gchar* dir) {
1099 State *s = &uzbl.state;
1102 xwin_str = itos((int)uzbl.xwin);
1104 str = g_strdup_printf
1105 ("%s/uzbl_fifo_%s", dir,
1106 s->instance_name ? s->instance_name : xwin_str);
1107 } else if (type == SOCKET) {
1108 str = g_strdup_printf
1109 ("%s/uzbl_socket_%s", dir,
1110 s->instance_name ? s->instance_name : xwin_str );
1117 control_fifo(GIOChannel *gio, GIOCondition condition) {
1118 if (uzbl.state.verbose)
1119 printf("triggered\n");
1124 if (condition & G_IO_HUP)
1125 g_error ("Fifo: Read end of pipe died!\n");
1128 g_error ("Fifo: GIOChannel broke\n");
1130 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1131 if (ret == G_IO_STATUS_ERROR) {
1132 g_error ("Fifo: Error reading: %s\n", err->message);
1136 parse_cmd_line(ctl_line);
1143 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1144 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1145 if (unlink(uzbl.comm.fifo_path) == -1)
1146 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1147 g_free(uzbl.comm.fifo_path);
1148 uzbl.comm.fifo_path = NULL;
1151 if (*dir == ' ') { /* space unsets the variable */
1155 GIOChannel *chan = NULL;
1156 GError *error = NULL;
1157 gchar *path = build_stream_name(FIFO, dir);
1159 if (!file_exists(path)) {
1160 if (mkfifo (path, 0666) == 0) {
1161 // 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.
1162 chan = g_io_channel_new_file(path, "r+", &error);
1164 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1165 if (uzbl.state.verbose)
1166 printf ("init_fifo: created successfully as %s\n", path);
1167 uzbl.comm.fifo_path = path;
1169 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1170 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1171 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1172 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1174 /* if we got this far, there was an error; cleanup */
1175 if (error) g_error_free (error);
1181 control_stdin(GIOChannel *gio, GIOCondition condition) {
1183 gchar *ctl_line = NULL;
1186 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1187 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1190 parse_cmd_line(ctl_line);
1198 GIOChannel *chan = NULL;
1199 GError *error = NULL;
1201 chan = g_io_channel_unix_new(fileno(stdin));
1203 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1204 g_error ("Stdin: could not add watch\n");
1206 if (uzbl.state.verbose)
1207 printf ("Stdin: watch added successfully\n");
1210 g_error ("Stdin: Error while opening: %s\n", error->message);
1212 if (error) g_error_free (error);
1216 control_socket(GIOChannel *chan) {
1217 struct sockaddr_un remote;
1218 char buffer[512], *ctl_line;
1220 int sock, clientsock, n, done;
1223 sock = g_io_channel_unix_get_fd(chan);
1225 memset (buffer, 0, sizeof (buffer));
1227 t = sizeof (remote);
1228 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1232 memset (temp, 0, sizeof (temp));
1233 n = recv (clientsock, temp, 128, 0);
1235 buffer[strlen (buffer)] = '\0';
1239 strcat (buffer, temp);
1242 if (strcmp (buffer, "\n") < 0) {
1243 buffer[strlen (buffer) - 1] = '\0';
1245 buffer[strlen (buffer)] = '\0';
1248 ctl_line = g_strdup(buffer);
1249 parse_cmd_line (ctl_line);
1252 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1253 GError *error = NULL;
1256 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1257 if (ret == G_IO_STATUS_ERROR)
1258 g_error ("Error reading: %s\n", error->message);
1260 printf("Got line %s (%u bytes) \n",ctl_line, len);
1262 parse_line(ctl_line);
1270 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1271 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1272 if (unlink(uzbl.comm.socket_path) == -1)
1273 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1274 g_free(uzbl.comm.socket_path);
1275 uzbl.comm.socket_path = NULL;
1283 GIOChannel *chan = NULL;
1285 struct sockaddr_un local;
1286 gchar *path = build_stream_name(SOCKET, dir);
1288 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1290 local.sun_family = AF_UNIX;
1291 strcpy (local.sun_path, path);
1292 unlink (local.sun_path);
1294 len = strlen (local.sun_path) + sizeof (local.sun_family);
1295 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1296 if (uzbl.state.verbose)
1297 printf ("init_socket: opened in %s\n", path);
1300 if( (chan = g_io_channel_unix_new(sock)) ) {
1301 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1302 uzbl.comm.socket_path = path;
1305 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1307 /* if we got this far, there was an error; cleanup */
1314 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1315 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1317 // this function may be called very early when the templates are not set (yet), hence the checks
1319 update_title (void) {
1320 Behaviour *b = &uzbl.behave;
1323 if (b->show_status) {
1324 if (b->title_format_short) {
1325 parsed = expand_template(b->title_format_short);
1326 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1329 if (b->status_format) {
1330 parsed = expand_template(b->status_format);
1331 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1334 if (b->status_background) {
1336 gdk_color_parse (b->status_background, &color);
1337 //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)
1338 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1341 if (b->title_format_long) {
1342 parsed = expand_template(b->title_format_long);
1343 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1350 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1352 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1356 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1357 || 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)
1360 /* turn off insert mode (if always_insert_mode is not used) */
1361 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1362 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1367 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1370 if (event->keyval == GDK_Escape) {
1371 g_string_truncate(uzbl.state.keycmd, 0);
1376 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1377 if (event->keyval == GDK_Insert) {
1379 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1380 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1382 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1385 g_string_append (uzbl.state.keycmd, str);
1392 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1393 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1397 gboolean key_ret = FALSE;
1398 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1400 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1402 run_keycmd(key_ret);
1404 if (key_ret) return (!uzbl.behave.insert_mode);
1409 run_keycmd(const gboolean key_ret) {
1410 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1412 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1413 g_string_truncate(uzbl.state.keycmd, 0);
1414 parse_command(action->name, action->param);
1418 /* try if it's an incremental keycmd or one that takes args, and run it */
1419 GString* short_keys = g_string_new ("");
1420 GString* short_keys_inc = g_string_new ("");
1422 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1423 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1424 g_string_assign(short_keys_inc, short_keys->str);
1425 g_string_append_c(short_keys, '_');
1426 g_string_append_c(short_keys_inc, '*');
1428 gboolean exec_now = FALSE;
1429 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1430 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1431 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1432 if (key_ret) { /* just quit the incremental command on return */
1433 g_string_truncate(uzbl.state.keycmd, 0);
1435 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1439 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1440 GString* actionname = g_string_new ("");
1441 GString* actionparam = g_string_new ("");
1442 g_string_erase (parampart, 0, i+1);
1444 g_string_printf (actionname, action->name, parampart->str);
1446 g_string_printf (actionparam, action->param, parampart->str);
1447 parse_command(actionname->str, actionparam->str);
1448 g_string_free (actionname, TRUE);
1449 g_string_free (actionparam, TRUE);
1450 g_string_free (parampart, TRUE);
1452 g_string_truncate(uzbl.state.keycmd, 0);
1456 g_string_truncate(short_keys, short_keys->len - 1);
1458 g_string_free (short_keys, TRUE);
1459 g_string_free (short_keys_inc, TRUE);
1466 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1467 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
1469 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1470 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1472 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1473 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1474 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1475 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1476 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1477 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1478 g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1479 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1480 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1481 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1483 return scrolled_window;
1490 g->mainbar = gtk_hbox_new (FALSE, 0);
1492 g->mainbar_label = gtk_label_new ("");
1493 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1494 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1495 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1496 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1497 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1502 GtkWidget* create_window () {
1503 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1504 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1505 gtk_widget_set_name (window, "Uzbl browser");
1506 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1512 add_binding (const gchar *key, const gchar *act) {
1513 char **parts = g_strsplit(act, " ", 2);
1520 if (uzbl.state.verbose)
1521 printf ("Binding %-10s : %s\n", key, act);
1523 action = new_action(parts[0], parts[1]);
1524 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1530 get_xdg_var (XDG_Var xdg) {
1531 const gchar* actual_value = getenv (xdg.environmental);
1532 const gchar* home = getenv ("HOME");
1534 gchar* return_value = str_replace ("~", home, actual_value);
1536 if (! actual_value || strcmp (actual_value, "") == 0) {
1537 if (xdg.default_value) {
1538 return_value = str_replace ("~", home, xdg.default_value);
1540 return_value = NULL;
1543 return return_value;
1547 find_xdg_file (int xdg_type, char* filename) {
1548 /* xdg_type = 0 => config
1549 xdg_type = 1 => data
1550 xdg_type = 2 => cache*/
1552 gchar* temporary_file = malloc (1024);
1553 gchar* temporary_string = NULL;
1557 buf = get_xdg_var (XDG[xdg_type]);
1558 strcpy (temporary_file, buf);
1559 strcat (temporary_file, filename);
1562 if (! file_exists (temporary_file) && xdg_type != 2) {
1563 buf = get_xdg_var (XDG[3 + xdg_type]);
1564 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1567 while (temporary_string && ! file_exists (temporary_file)) {
1568 strcpy (temporary_file, temporary_string);
1569 strcat (temporary_file, filename);
1570 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1574 if (file_exists (temporary_file)) {
1575 return temporary_file;
1583 State *s = &uzbl.state;
1584 Network *n = &uzbl.net;
1586 uzbl.behave.reset_command_mode = 1;
1588 if (!s->config_file) {
1589 s->config_file = find_xdg_file (0, "/uzbl/config");
1592 if (s->config_file) {
1593 GIOChannel *chan = NULL;
1594 gchar *readbuf = NULL;
1597 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1600 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1601 == G_IO_STATUS_NORMAL) {
1602 parse_cmd_line(readbuf);
1606 g_io_channel_unref (chan);
1607 if (uzbl.state.verbose)
1608 printf ("Config %s loaded\n", s->config_file);
1610 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1613 if (uzbl.state.verbose)
1614 printf ("No configuration file loaded.\n");
1616 if (!uzbl.behave.status_format)
1617 set_var_value("status_format", STATUS_DEFAULT);
1618 if (!uzbl.behave.title_format_long)
1619 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1620 if (!uzbl.behave.title_format_short)
1621 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1624 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1628 set_useragent(gchar *val) {
1633 gchar *ua = expand_template(val);
1635 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1639 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1642 if (!uzbl.behave.cookie_handler) return;
1644 gchar * stdout = NULL;
1645 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1646 GString* args = g_string_new ("");
1647 SoupURI * soup_uri = soup_message_get_uri(msg);
1648 g_string_printf (args, "GET %s %s", soup_uri->host, soup_uri->path);
1649 run_command(uzbl.behave.cookie_handler, args->str, TRUE, &stdout);
1651 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1653 g_string_free(args, TRUE);
1657 save_cookies (SoupMessage *msg, gpointer user_data){
1661 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1662 cookie = soup_cookie_to_set_cookie_header(ck->data);
1663 GString* args = g_string_new ("");
1664 SoupURI * soup_uri = soup_message_get_uri(msg);
1665 g_string_printf (args, "PUT %s %s \"%s\"", soup_uri->host, soup_uri->path, cookie);
1666 run_command(uzbl.behave.cookie_handler, args->str, FALSE, NULL);
1667 g_string_free(args, TRUE);
1675 main (int argc, char* argv[]) {
1676 gtk_init (&argc, &argv);
1677 if (!g_thread_supported ())
1678 g_thread_init (NULL);
1680 strcpy(uzbl.state.executable_path,argv[0]);
1682 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1683 g_option_context_add_main_entries (context, entries, NULL);
1684 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1685 g_option_context_parse (context, &argc, &argv, NULL);
1686 g_option_context_free(context);
1687 /* initialize hash table */
1688 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1690 uzbl.net.soup_session = webkit_get_default_session();
1691 uzbl.state.keycmd = g_string_new("");
1693 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1694 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1695 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1696 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1698 if(uname(&uzbl.state.unameinfo) == -1)
1699 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1704 make_var_to_name_hash();
1707 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1709 uzbl.gui.scrolled_win = create_browser();
1712 /* initial packing */
1713 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1714 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1716 uzbl.gui.main_window = create_window ();
1717 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1720 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1721 gtk_widget_show_all (uzbl.gui.main_window);
1722 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1724 if (uzbl.state.verbose) {
1725 printf("Uzbl start location: %s\n", argv[0]);
1726 printf("window_id %i\n",(int) uzbl.xwin);
1727 printf("pid %i\n", getpid ());
1728 printf("name: %s\n", uzbl.state.instance_name);
1731 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1732 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1733 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1734 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1735 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1739 if (!uzbl.behave.show_status)
1740 gtk_widget_hide(uzbl.gui.mainbar);
1747 load_uri (uzbl.gui.web_view, uzbl.state.uri);
1753 return EXIT_SUCCESS;
1756 /* vi: set et ts=4: */