disable cookies again
[uzbl-00z.git] / uzbl.c
blob5f997f74f20bead572dd9aaad686011d381d4e84
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.
6 /*
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
12 * are met:
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
36 #include <gtk/gtk.h>
37 #include <gdk/gdkx.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <sys/un.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <errno.h>
50 #include <string.h>
51 #include <fcntl.h>
52 #include <sys/socket.h>
53 #include <sys/un.h>
54 #include <libsoup/soup.h>
55 #include <signal.h>
56 #include "uzbl.h"
59 static Uzbl uzbl;
61 /* define names and pointers to all config specific variables */
62 const struct {
63 char *name;
64 void **ptr;
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 },
92 { NULL, NULL }
93 }, *n2v_p = var_name_to_ptr;
95 const struct {
96 char *key;
97 guint mask;
98 } modkeys[] = {
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)
115 { NULL, 0 }
118 /* construct a hash from the var_name_to_ptr array for quick access */
119 static void
120 make_var_to_name_hash() {
121 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
122 while(n2v_p->name) {
123 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, n2v_p->ptr);
124 n2v_p++;
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 --- */
142 char *
143 itos(int val) {
144 char tmp[20];
146 snprintf(tmp, sizeof(tmp), "%i", val);
147 return g_strdup(tmp);
150 static char *
151 str_replace (const char* search, const char* replace, const char* string) {
152 return g_strjoinv (replace, g_strsplit (string, search, -1));
155 static sigfunc*
156 setup_signal(int signr, sigfunc *shandler) {
157 struct sigaction nh, oh;
159 nh.sa_handler = shandler;
160 sigemptyset(&nh.sa_mask);
161 nh.sa_flags = 0;
163 if(sigaction(signr, &nh, &oh) < 0)
164 return SIG_ERR;
166 return NULL;
169 static void
170 clean_up(void) {
171 if (uzbl.behave.fifo_dir)
172 unlink (uzbl.comm.fifo_path);
173 if (uzbl.behave.socket_dir)
174 unlink (uzbl.comm.socket_path);
176 g_string_free(uzbl.state.keycmd, TRUE);
177 g_hash_table_destroy(uzbl.bindings);
178 g_hash_table_destroy(uzbl.behave.commands);
182 /* --- SIGNAL HANDLER --- */
184 static void
185 catch_sigterm(int s) {
186 (void) s;
187 clean_up();
190 static void
191 catch_sigint(int s) {
192 (void) s;
193 clean_up();
194 exit(EXIT_SUCCESS);
197 /* --- CALLBACKS --- */
199 static gboolean
200 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
201 (void) web_view;
202 (void) frame;
203 (void) navigation_action;
204 (void) policy_decision;
205 (void) user_data;
206 const gchar* uri = webkit_network_request_get_uri (request);
207 if (uzbl.state.verbose)
208 printf("New window requested -> %s \n", uri);
209 new_window_load_uri(uri);
210 return (FALSE);
213 WebKitWebView*
214 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
215 (void) web_view;
216 (void) frame;
217 (void) user_data;
218 if (uzbl.state.selected_url[0]!=0) {
219 if (uzbl.state.verbose)
220 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
221 new_window_load_uri(uzbl.state.selected_url);
222 } else {
223 if (uzbl.state.verbose)
224 printf("New web view -> %s\n","Nothing to open, exiting");
226 return (NULL);
229 static gboolean
230 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
231 (void) web_view;
232 (void) user_data;
233 if (uzbl.behave.download_handler) {
234 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
235 if (uzbl.state.verbose)
236 printf("Download -> %s\n",uri);
237 run_command(uzbl.behave.download_handler, uri, FALSE, NULL);
239 return (FALSE);
242 /* scroll a bar in a given direction */
243 static void
244 scroll (GtkAdjustment* bar, const char *param) {
245 gdouble amount;
246 gchar *end;
248 amount = g_ascii_strtod(param, &end);
250 if (*end)
251 fprintf(stderr, "found something after double: %s\n", end);
253 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
256 static void scroll_begin(WebKitWebView* page, const char *param) {
257 (void) page; (void) param;
258 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
261 static void scroll_end(WebKitWebView* page, const char *param) {
262 (void) page; (void) param;
263 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
264 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
267 static void scroll_vert(WebKitWebView* page, const char *param) {
268 (void) page;
269 scroll(uzbl.gui.bar_v, param);
272 static void scroll_horz(WebKitWebView* page, const char *param) {
273 (void) page;
274 scroll(uzbl.gui.bar_h, param);
277 static void
278 cmd_set_status() {
279 if (!uzbl.behave.show_status) {
280 gtk_widget_hide(uzbl.gui.mainbar);
281 } else {
282 gtk_widget_show(uzbl.gui.mainbar);
284 update_title();
287 static void
288 toggle_status_cb (WebKitWebView* page, const char *param) {
289 (void)page;
290 (void)param;
292 if (uzbl.behave.show_status) {
293 gtk_widget_hide(uzbl.gui.mainbar);
294 } else {
295 gtk_widget_show(uzbl.gui.mainbar);
297 uzbl.behave.show_status = !uzbl.behave.show_status;
298 update_title();
301 static void
302 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
303 (void) page;
304 (void) title;
305 (void) data;
306 //Set selected_url state variable
307 uzbl.state.selected_url[0] = '\0';
308 if (link) {
309 strcpy (uzbl.state.selected_url, link);
311 update_title();
314 static void
315 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
316 (void) web_view;
317 (void) web_frame;
318 (void) data;
319 if (uzbl.gui.main_title)
320 g_free (uzbl.gui.main_title);
321 uzbl.gui.main_title = g_strdup (title);
322 update_title();
325 static void
326 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
327 (void) page;
328 (void) data;
329 uzbl.gui.sbar.load_progress = progress;
330 update_title();
333 static void
334 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
335 (void) page;
336 (void) frame;
337 (void) data;
338 if (uzbl.behave.load_finish_handler) {
339 run_command(uzbl.behave.load_finish_handler, NULL, FALSE, NULL);
343 static void
344 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
345 (void) page;
346 (void) data;
347 free (uzbl.state.uri);
348 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
349 uzbl.state.uri = g_string_free (newuri, FALSE);
350 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
351 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
352 update_title();
354 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
357 static void
358 destroy_cb (GtkWidget* widget, gpointer data) {
359 (void) widget;
360 (void) data;
361 gtk_main_quit ();
364 static void
365 log_history_cb () {
366 if (uzbl.behave.history_handler) {
367 time_t rawtime;
368 struct tm * timeinfo;
369 char date [80];
370 time ( &rawtime );
371 timeinfo = localtime ( &rawtime );
372 strftime (date, 80, "%Y-%m-%d %H:%M:%S", timeinfo);
373 GString* args = g_string_new ("");
374 g_string_printf (args, "'%s'", date);
375 run_command(uzbl.behave.history_handler, args->str, FALSE, NULL);
376 g_string_free (args, TRUE);
381 /* VIEW funcs (little webkit wrappers) */
382 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
383 VIEWFUNC(reload)
384 VIEWFUNC(reload_bypass_cache)
385 VIEWFUNC(stop_loading)
386 VIEWFUNC(zoom_in)
387 VIEWFUNC(zoom_out)
388 VIEWFUNC(go_back)
389 VIEWFUNC(go_forward)
390 #undef VIEWFUNC
392 /* -- command to callback/function map for things we cannot attach to any signals */
393 // TODO: reload
395 static struct {char *name; Command command;} cmdlist[] =
397 { "back", view_go_back },
398 { "forward", view_go_forward },
399 { "scroll_vert", scroll_vert },
400 { "scroll_horz", scroll_horz },
401 { "scroll_begin", scroll_begin },
402 { "scroll_end", scroll_end },
403 { "reload", view_reload, },
404 { "reload_ign_cache", view_reload_bypass_cache},
405 { "stop", view_stop_loading, },
406 { "zoom_in", view_zoom_in, }, //Can crash (when max zoom reached?).
407 { "zoom_out", view_zoom_out, },
408 { "uri", load_uri },
409 { "script", run_js },
410 { "toggle_status", toggle_status_cb },
411 { "spawn", spawn },
412 { "sh", spawn_sh },
413 { "exit", close_uzbl },
414 { "search", search_forward_text },
415 { "search_reverse", search_reverse_text },
416 { "insert_mode", set_insert_mode },
417 { "runcmd", runcmd }
420 static void
421 commands_hash(void)
423 unsigned int i;
424 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
426 for (i = 0; i < LENGTH(cmdlist); i++)
427 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
430 /* -- CORE FUNCTIONS -- */
432 void
433 free_action(gpointer act) {
434 Action *action = (Action*)act;
435 g_free(action->name);
436 if (action->param)
437 g_free(action->param);
438 g_free(action);
441 Action*
442 new_action(const gchar *name, const gchar *param) {
443 Action *action = g_new(Action, 1);
445 action->name = g_strdup(name);
446 if (param)
447 action->param = g_strdup(param);
448 else
449 action->param = NULL;
451 return action;
454 static bool
455 file_exists (const char * filename) {
456 return (access(filename, F_OK) == 0);
459 void
460 set_insert_mode(WebKitWebView *page, const gchar *param) {
461 (void)page;
462 (void)param;
464 uzbl.behave.insert_mode = TRUE;
465 update_title();
468 static void
469 load_uri (WebKitWebView * web_view, const gchar *param) {
470 if (param) {
471 GString* newuri = g_string_new (param);
472 if (g_strrstr (param, "://") == NULL)
473 g_string_prepend (newuri, "http://");
474 /* if we do handle cookies, ask our handler for them */
475 webkit_web_view_load_uri (web_view, newuri->str);
476 g_string_free (newuri, TRUE);
480 static void
481 run_js (WebKitWebView * web_view, const gchar *param) {
482 if (param)
483 webkit_web_view_execute_script (web_view, param);
486 static void
487 search_text (WebKitWebView *page, const char *param, const gboolean forward) {
488 if ((param) && (param[0] != '\0')) {
489 strcpy(uzbl.state.searchtx, param);
491 if (uzbl.state.searchtx[0] != '\0') {
492 if (uzbl.state.verbose)
493 printf ("Searching: %s\n", uzbl.state.searchtx);
494 webkit_web_view_unmark_text_matches (page);
495 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
496 webkit_web_view_set_highlight_text_matches (page, TRUE);
497 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
501 static void
502 search_forward_text (WebKitWebView *page, const char *param) {
503 search_text(page, param, TRUE);
506 static void
507 search_reverse_text (WebKitWebView *page, const char *param) {
508 search_text(page, param, FALSE);
511 static void
512 new_window_load_uri (const gchar * uri) {
513 GString* to_execute = g_string_new ("");
514 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
515 int i;
516 for (i = 0; entries[i].long_name != NULL; i++) {
517 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
518 gchar** str = (gchar**)entries[i].arg_data;
519 if (*str!=NULL) {
520 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
524 if (uzbl.state.verbose)
525 printf("\n%s\n", to_execute->str);
526 g_spawn_command_line_async (to_execute->str, NULL);
527 g_string_free (to_execute, TRUE);
530 static void
531 close_uzbl (WebKitWebView *page, const char *param) {
532 (void)page;
533 (void)param;
534 gtk_main_quit ();
537 /* --Statusbar functions-- */
538 static char*
539 build_progressbar_ascii(int percent) {
540 int width=10;
541 int i;
542 double l;
543 GString *bar = g_string_new("");
545 l = (double)percent*((double)width/100.);
546 l = (int)(l+.5)>=(int)l ? l+.5 : l;
548 for(i=0; i<(int)l; i++)
549 g_string_append(bar, "=");
551 for(; i<width; i++)
552 g_string_append(bar, "·");
554 return g_string_free(bar, FALSE);
557 static void
558 setup_scanner() {
559 const GScannerConfig scan_config = {
561 "\t\r\n"
562 ) /* cset_skip_characters */,
564 G_CSET_a_2_z
565 "_#"
566 G_CSET_A_2_Z
567 ) /* cset_identifier_first */,
569 G_CSET_a_2_z
570 "_0123456789"
571 G_CSET_A_2_Z
572 G_CSET_LATINS
573 G_CSET_LATINC
574 ) /* cset_identifier_nth */,
575 ( "" ) /* cpair_comment_single */,
577 TRUE /* case_sensitive */,
579 FALSE /* skip_comment_multi */,
580 FALSE /* skip_comment_single */,
581 FALSE /* scan_comment_multi */,
582 TRUE /* scan_identifier */,
583 TRUE /* scan_identifier_1char */,
584 FALSE /* scan_identifier_NULL */,
585 TRUE /* scan_symbols */,
586 FALSE /* scan_binary */,
587 FALSE /* scan_octal */,
588 FALSE /* scan_float */,
589 FALSE /* scan_hex */,
590 FALSE /* scan_hex_dollar */,
591 FALSE /* scan_string_sq */,
592 FALSE /* scan_string_dq */,
593 TRUE /* numbers_2_int */,
594 FALSE /* int_2_float */,
595 FALSE /* identifier_2_string */,
596 FALSE /* char_2_token */,
597 FALSE /* symbol_2_token */,
598 TRUE /* scope_0_fallback */,
599 FALSE,
600 TRUE
603 uzbl.scan = g_scanner_new(&scan_config);
604 while(symp->symbol_name) {
605 g_scanner_scope_add_symbol(uzbl.scan, 0,
606 symp->symbol_name,
607 GINT_TO_POINTER(symp->symbol_token));
608 symp++;
612 static gchar *
613 expand_template(const char *template) {
614 if(!template) return NULL;
616 GTokenType token = G_TOKEN_NONE;
617 GString *ret = g_string_new("");
618 char *buf=NULL;
619 int sym;
621 g_scanner_input_text(uzbl.scan, template, strlen(template));
622 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
623 token = g_scanner_get_next_token(uzbl.scan);
625 if(token == G_TOKEN_SYMBOL) {
626 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
627 switch(sym) {
628 case SYM_URI:
629 g_string_append(ret,
630 uzbl.state.uri?
631 g_markup_printf_escaped("%s", uzbl.state.uri):"");
632 break;
633 case SYM_LOADPRGS:
634 buf = itos(uzbl.gui.sbar.load_progress);
635 g_string_append(ret, buf);
636 free(buf);
637 break;
638 case SYM_LOADPRGSBAR:
639 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
640 g_string_append(ret, buf);
641 g_free(buf);
642 break;
643 case SYM_TITLE:
644 g_string_append(ret,
645 uzbl.gui.main_title?
646 g_markup_printf_escaped("%s", uzbl.gui.main_title):"");
647 break;
648 case SYM_SELECTED_URI:
649 g_string_append(ret,
650 uzbl.state.selected_url?
651 g_markup_printf_escaped("%s", uzbl.state.selected_url):"");
652 break;
653 case SYM_NAME:
654 buf = itos(uzbl.xwin);
655 g_string_append(ret,
656 uzbl.state.instance_name?uzbl.state.instance_name:buf);
657 free(buf);
658 break;
659 case SYM_KEYCMD:
660 g_string_append(ret,
661 uzbl.state.keycmd->str ?
662 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):"");
663 break;
664 case SYM_MODE:
665 g_string_append(ret,
666 uzbl.behave.insert_mode?"[I]":"[C]");
667 break;
668 case SYM_MSG:
669 g_string_append(ret,
670 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
671 break;
672 /* useragent syms */
673 case SYM_WK_MAJ:
674 buf = itos(WEBKIT_MAJOR_VERSION);
675 g_string_append(ret, buf);
676 free(buf);
677 break;
678 case SYM_WK_MIN:
679 buf = itos(WEBKIT_MINOR_VERSION);
680 g_string_append(ret, buf);
681 free(buf);
682 break;
683 case SYM_WK_MIC:
684 buf = itos(WEBKIT_MICRO_VERSION);
685 g_string_append(ret, buf);
686 free(buf);
687 break;
688 case SYM_SYSNAME:
689 g_string_append(ret, uzbl.state.unameinfo.sysname);
690 break;
691 case SYM_NODENAME:
692 g_string_append(ret, uzbl.state.unameinfo.nodename);
693 break;
694 case SYM_KERNREL:
695 g_string_append(ret, uzbl.state.unameinfo.release);
696 break;
697 case SYM_KERNVER:
698 g_string_append(ret, uzbl.state.unameinfo.version);
699 break;
700 case SYM_ARCHSYS:
701 g_string_append(ret, uzbl.state.unameinfo.machine);
702 break;
703 case SYM_ARCHUZBL:
704 g_string_append(ret, ARCH);
705 break;
706 #ifdef _GNU_SOURCE
707 case SYM_DOMAINNAME:
708 g_string_append(ret, uzbl.state.unameinfo.domainname);
709 break;
710 #endif
711 case SYM_COMMIT:
712 g_string_append(ret, COMMIT);
713 break;
714 default:
715 break;
718 else if(token == G_TOKEN_INT) {
719 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
720 g_string_append(ret, buf);
721 free(buf);
723 else if(token == G_TOKEN_IDENTIFIER) {
724 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
726 else if(token == G_TOKEN_CHAR) {
727 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
731 return g_string_free(ret, FALSE);
733 /* --End Statusbar functions-- */
736 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
737 static gboolean
738 run_command (const char *command, const char *args, const gboolean sync, char **stdout) {
739 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
740 GString *to_execute = g_string_new ("");
741 GError *err = NULL;
742 gchar *cmd = g_strstrip(g_strdup(command));
743 gchar *qcfg = (uzbl.state.config_file ? g_shell_quote(uzbl.state.config_file) : g_strdup("''"));
744 gchar *qfifo = (uzbl.comm.fifo_path ? g_shell_quote(uzbl.comm.fifo_path) : g_strdup("''"));
745 gchar *qsock = (uzbl.comm.socket_path ? g_shell_quote(uzbl.comm.socket_path) : g_strdup("''"));
746 gchar *quri = (uzbl.state.uri ? g_shell_quote(uzbl.state.uri) : g_strdup("''"));
747 gchar *qtitle = (uzbl.gui.main_title ? g_shell_quote(uzbl.gui.main_title) : g_strdup("''"));
749 gboolean result;
750 g_string_printf (to_execute, "%s %s '%i' '%i' %s %s",
751 cmd, qcfg, (int) getpid(), (int) uzbl.xwin, qfifo, qsock);
752 g_string_append_printf (to_execute, " %s %s", quri, qtitle);
753 if(args) g_string_append_printf (to_execute, " %s", args);
755 if (sync) {
756 result = g_spawn_command_line_sync (to_execute->str, stdout, NULL, NULL, &err);
757 } else result = g_spawn_command_line_async (to_execute->str, &err);
758 if (uzbl.state.verbose)
759 printf("Called %s. Result: %s\n", to_execute->str, (result ? "TRUE" : "FALSE" ));
760 g_string_free (to_execute, TRUE);
761 if (err) {
762 g_printerr("error on run_command: %s\n", err->message);
763 g_error_free (err);
766 g_free (qcfg);
767 g_free (qfifo);
768 g_free (qsock);
769 g_free (quri);
770 g_free (qtitle);
771 g_free (cmd);
772 return result;
775 static void
776 spawn(WebKitWebView *web_view, const char *param) {
777 (void)web_view;
779 TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
780 gchar** cmd = g_strsplit(param, " ", 2);
781 gchar * args = NULL;
782 if (cmd[1]) {
783 args = g_shell_quote(cmd[1]);
785 if (cmd) {
786 run_command(cmd[0], args, FALSE, NULL);
788 if (args) {
789 g_free(args);
792 run_command(param, NULL, FALSE, NULL);
795 static void
796 spawn_sh(WebKitWebView *web_view, const char *param) {
797 (void)web_view;
798 gchar *cmd = g_strdup_printf(uzbl.behave.shell_cmd, param);
799 spawn(NULL, cmd);
800 g_free(cmd);
803 static void
804 parse_command(const char *cmd, const char *param) {
805 Command c;
807 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd)))
808 c(uzbl.gui.web_view, param);
809 else
810 fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
813 /* command parser */
814 static void
815 setup_regex() {
816 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
817 G_REGEX_OPTIMIZE, 0, NULL);
818 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
819 G_REGEX_OPTIMIZE, 0, NULL);
820 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
821 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
822 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
823 G_REGEX_OPTIMIZE, 0, NULL);
824 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
825 G_REGEX_OPTIMIZE, 0, NULL);
828 static gboolean
829 get_var_value(gchar *name) {
830 void **p = NULL;
832 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
833 if(var_is("status_format", name)
834 || var_is("useragent", name)
835 || var_is("title_format_short", name)
836 || var_is("title_format_long", name)) {
837 printf("VAR: %s VALUE: %s\n", name, (char *)*p);
838 } else printf("VAR: %s VALUE: %d\n", name, (int)*p);
840 return TRUE;
843 static void
844 set_proxy_url() {
845 SoupURI *suri;
847 if(*uzbl.net.proxy_url == ' '
848 || uzbl.net.proxy_url == NULL) {
849 soup_session_remove_feature_by_type(uzbl.net.soup_session,
850 (GType) SOUP_SESSION_PROXY_URI);
852 else {
853 suri = soup_uri_new(uzbl.net.proxy_url);
854 g_object_set(G_OBJECT(uzbl.net.soup_session),
855 SOUP_SESSION_PROXY_URI,
856 suri, NULL);
857 soup_uri_free(suri);
859 return;
863 static void
864 move_statusbar() {
865 gtk_widget_ref(uzbl.gui.scrolled_win);
866 gtk_widget_ref(uzbl.gui.mainbar);
867 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
868 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
870 if(uzbl.behave.status_top) {
871 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
872 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
874 else {
875 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
876 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
878 gtk_widget_unref(uzbl.gui.scrolled_win);
879 gtk_widget_unref(uzbl.gui.mainbar);
880 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
883 static gboolean
884 var_is(const char *x, const char *y) {
885 return (strcmp(x, y) == 0 ? TRUE : FALSE );
888 static gboolean
889 set_var_value(gchar *name, gchar *val) {
890 void **p = NULL;
891 char *endp = NULL;
893 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
894 if(var_is("status_message", name)
895 || var_is("status_background", name)
896 || var_is("status_format", name)
897 || var_is("title_format_long", name)
898 || var_is("title_format_short", name)
899 || var_is("load_finish_handler", name)
900 || var_is("history_handler", name)
901 || var_is("download_handler", name)
902 || var_is("cookie_handler", name)) {
903 if(*p)
904 free(*p);
905 *p = g_strdup(val);
906 update_title();
908 else if(var_is("uri", name)) {
909 if(*p) free(*p);
910 *p = g_strdup(val);
911 load_uri(uzbl.gui.web_view, (const gchar*)*p);
913 else if(var_is("proxy_url", name)) {
914 if(*p) free(*p);
915 *p = g_strdup(val);
916 set_proxy_url();
918 else if(var_is("fifo_dir", name)) {
919 if(*p) free(*p);
920 *p = init_fifo(g_strdup(val));
922 else if(var_is("socket_dir", name)) {
923 if(*p) free(*p);
924 *p = init_socket(g_strdup(val));
926 else if(var_is("modkey", name)) {
927 if(*p) free(*p);
928 int i;
929 *p = g_utf8_strup(val, -1);
930 uzbl.behave.modmask = 0;
931 for (i = 0; modkeys[i].key != NULL; i++) {
932 if (g_strrstr(*p, modkeys[i].key))
933 uzbl.behave.modmask |= modkeys[i].mask;
936 else if(var_is("useragent", name)) {
937 if(*p) free(*p);
938 *p = set_useragent(g_strdup(val));
940 else if(var_is("shell_cmd", name)) {
941 if(*p) free(*p);
942 *p = g_strdup(val);
944 /* variables that take int values */
945 else {
946 int *ip = (int *)p;
947 *ip = (int)strtoul(val, &endp, 10);
949 if(var_is("show_status", name)) {
950 cmd_set_status();
952 else if(var_is("always_insert_mode", name)) {
953 uzbl.behave.insert_mode =
954 uzbl.behave.always_insert_mode ? TRUE : FALSE;
955 update_title();
957 else if (var_is("max_conns", name)) {
958 g_object_set(G_OBJECT(uzbl.net.soup_session),
959 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
961 else if (var_is("max_conns_host", name)) {
962 g_object_set(G_OBJECT(uzbl.net.soup_session),
963 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
965 else if (var_is("http_debug", name)) {
966 //soup_session_remove_feature
967 // (uzbl.net.soup_session, uzbl.net.soup_logger);
968 soup_session_remove_feature
969 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
970 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
971 /*g_free(uzbl.net.soup_logger);*/
973 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
974 soup_session_add_feature(uzbl.net.soup_session,
975 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
977 else if (var_is("status_top", name)) {
978 move_statusbar();
980 else if (var_is("default_font_size", name)) {
981 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
982 g_object_set (G_OBJECT(ws), "default-font-size", *ip, NULL);
984 else if (var_is("minimum_font_size", name)) {
985 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
986 g_object_set (G_OBJECT(ws), "minimum-font-size", *ip, NULL);
990 return TRUE;
993 static void
994 runcmd(WebKitWebView* page, const char *param) {
995 (void) page;
996 parse_cmd_line(param);
999 static void
1000 parse_cmd_line(const char *ctl_line) {
1001 gchar **tokens;
1003 /* SET command */
1004 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1005 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1006 if(tokens[0][0] == 0) {
1007 set_var_value(tokens[1], tokens[2]);
1008 g_strfreev(tokens);
1010 else
1011 printf("Error in command: %s\n", tokens[0]);
1013 /* GET command */
1014 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1015 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1016 if(tokens[0][0] == 0) {
1017 get_var_value(tokens[1]);
1018 g_strfreev(tokens);
1020 else
1021 printf("Error in command: %s\n", tokens[0]);
1023 /* BIND command */
1024 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1025 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1026 if(tokens[0][0] == 0) {
1027 add_binding(tokens[1], tokens[2]);
1028 g_strfreev(tokens);
1030 else
1031 printf("Error in command: %s\n", tokens[0]);
1033 /* ACT command */
1034 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1035 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1036 if(tokens[0][0] == 0) {
1037 parse_command(tokens[1], tokens[2]);
1038 g_strfreev(tokens);
1040 else
1041 printf("Error in command: %s\n", tokens[0]);
1043 /* KEYCMD command */
1044 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1045 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1046 if(tokens[0][0] == 0) {
1047 /* should incremental commands want each individual "keystroke"
1048 sent in a loop or the whole string in one go like now? */
1049 g_string_assign(uzbl.state.keycmd, tokens[1]);
1050 run_keycmd(FALSE);
1051 update_title();
1052 g_strfreev(tokens);
1055 /* Comments */
1056 else if( (ctl_line[0] == '#')
1057 || (ctl_line[0] == ' ')
1058 || (ctl_line[0] == '\n'))
1059 ; /* ignore these lines */
1060 else
1061 printf("Command not understood (%s)\n", ctl_line);
1063 return;
1066 static gchar*
1067 build_stream_name(int type, const gchar* dir) {
1068 char *xwin_str;
1069 State *s = &uzbl.state;
1070 gchar *str;
1072 xwin_str = itos((int)uzbl.xwin);
1073 if (type == FIFO) {
1074 str = g_strdup_printf
1075 ("%s/uzbl_fifo_%s", dir,
1076 s->instance_name ? s->instance_name : xwin_str);
1077 } else if (type == SOCKET) {
1078 str = g_strdup_printf
1079 ("%s/uzbl_socket_%s", dir,
1080 s->instance_name ? s->instance_name : xwin_str );
1082 g_free(xwin_str);
1083 return str;
1086 static gboolean
1087 control_fifo(GIOChannel *gio, GIOCondition condition) {
1088 if (uzbl.state.verbose)
1089 printf("triggered\n");
1090 gchar *ctl_line;
1091 GIOStatus ret;
1092 GError *err = NULL;
1094 if (condition & G_IO_HUP)
1095 g_error ("Fifo: Read end of pipe died!\n");
1097 if(!gio)
1098 g_error ("Fifo: GIOChannel broke\n");
1100 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1101 if (ret == G_IO_STATUS_ERROR) {
1102 g_error ("Fifo: Error reading: %s\n", err->message);
1103 g_error_free (err);
1106 parse_cmd_line(ctl_line);
1107 g_free(ctl_line);
1109 return TRUE;
1112 static gchar*
1113 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1114 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1115 if (unlink(uzbl.comm.fifo_path) == -1)
1116 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1117 g_free(uzbl.comm.fifo_path);
1118 uzbl.comm.fifo_path = NULL;
1121 if (*dir == ' ') { /* space unsets the variable */
1122 g_free(dir);
1123 return NULL;
1126 GIOChannel *chan = NULL;
1127 GError *error = NULL;
1128 gchar *path = build_stream_name(FIFO, dir);
1130 if (!file_exists(path)) {
1131 if (mkfifo (path, 0666) == 0) {
1132 // 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.
1133 chan = g_io_channel_new_file(path, "r+", &error);
1134 if (chan) {
1135 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1136 if (uzbl.state.verbose)
1137 printf ("init_fifo: created successfully as %s\n", path);
1138 uzbl.comm.fifo_path = path;
1139 return dir;
1140 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1141 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1142 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1143 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1145 /* if we got this far, there was an error; cleanup */
1146 if (error) g_error_free (error);
1147 g_free(path);
1148 g_free(dir);
1149 return NULL;
1152 static gboolean
1153 control_stdin(GIOChannel *gio, GIOCondition condition) {
1154 gchar *ctl_line = NULL;
1155 gsize ctl_line_len = 0;
1156 GIOStatus ret;
1158 if (condition & G_IO_HUP) {
1159 ret = g_io_channel_shutdown (gio, FALSE, NULL);
1160 return FALSE;
1163 ret = g_io_channel_read_line(gio, &ctl_line, &ctl_line_len, NULL, NULL);
1164 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1165 return FALSE;
1167 parse_cmd_line(ctl_line);
1168 g_free(ctl_line);
1170 return TRUE;
1173 static void
1174 create_stdin () {
1175 GIOChannel *chan = NULL;
1176 GError *error = NULL;
1178 chan = g_io_channel_unix_new(fileno(stdin));
1179 if (chan) {
1180 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1181 g_error ("Stdin: could not add watch\n");
1182 } else {
1183 if (uzbl.state.verbose)
1184 printf ("Stdin: watch added successfully\n");
1186 } else {
1187 g_error ("Stdin: Error while opening: %s\n", error->message);
1189 if (error) g_error_free (error);
1192 static gboolean
1193 control_socket(GIOChannel *chan) {
1194 struct sockaddr_un remote;
1195 char buffer[512], *ctl_line;
1196 char temp[128];
1197 int sock, clientsock, n, done;
1198 unsigned int t;
1200 sock = g_io_channel_unix_get_fd(chan);
1202 memset (buffer, 0, sizeof (buffer));
1204 t = sizeof (remote);
1205 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1207 done = 0;
1208 do {
1209 memset (temp, 0, sizeof (temp));
1210 n = recv (clientsock, temp, 128, 0);
1211 if (n == 0) {
1212 buffer[strlen (buffer)] = '\0';
1213 done = 1;
1215 if (!done)
1216 strcat (buffer, temp);
1217 } while (!done);
1219 if (strcmp (buffer, "\n") < 0) {
1220 buffer[strlen (buffer) - 1] = '\0';
1221 } else {
1222 buffer[strlen (buffer)] = '\0';
1224 close (clientsock);
1225 ctl_line = g_strdup(buffer);
1226 parse_cmd_line (ctl_line);
1229 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1230 GError *error = NULL;
1231 gsize len;
1232 GIOStatus ret;
1233 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1234 if (ret == G_IO_STATUS_ERROR)
1235 g_error ("Error reading: %s\n", error->message);
1237 printf("Got line %s (%u bytes) \n",ctl_line, len);
1238 if(ctl_line) {
1239 parse_line(ctl_line);
1242 g_free(ctl_line);
1243 return TRUE;
1246 static gchar*
1247 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1248 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1249 if (unlink(uzbl.comm.socket_path) == -1)
1250 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1251 g_free(uzbl.comm.socket_path);
1252 uzbl.comm.socket_path = NULL;
1255 if (*dir == ' ') {
1256 g_free(dir);
1257 return NULL;
1260 GIOChannel *chan = NULL;
1261 int sock, len;
1262 struct sockaddr_un local;
1263 gchar *path = build_stream_name(SOCKET, dir);
1265 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1267 local.sun_family = AF_UNIX;
1268 strcpy (local.sun_path, path);
1269 unlink (local.sun_path);
1271 len = strlen (local.sun_path) + sizeof (local.sun_family);
1272 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1273 if (uzbl.state.verbose)
1274 printf ("init_socket: opened in %s\n", path);
1275 listen (sock, 5);
1277 if( (chan = g_io_channel_unix_new(sock)) ) {
1278 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1279 uzbl.comm.socket_path = path;
1280 return dir;
1282 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1284 /* if we got this far, there was an error; cleanup */
1285 g_free(path);
1286 g_free(dir);
1287 return NULL;
1291 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1292 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1294 // this function may be called very early when the templates are not set (yet), hence the checks
1295 static void
1296 update_title (void) {
1297 Behaviour *b = &uzbl.behave;
1298 gchar *parsed;
1300 if (b->show_status) {
1301 if (b->title_format_short) {
1302 parsed = expand_template(b->title_format_short);
1303 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1304 g_free(parsed);
1306 if (b->status_format) {
1307 parsed = expand_template(b->status_format);
1308 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1309 g_free(parsed);
1311 if (b->status_background) {
1312 GdkColor color;
1313 gdk_color_parse (b->status_background, &color);
1314 //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)
1315 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1317 } else {
1318 if (b->title_format_long) {
1319 parsed = expand_template(b->title_format_long);
1320 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1321 g_free(parsed);
1326 static gboolean
1327 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1329 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1331 (void) page;
1333 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1334 || 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)
1335 return FALSE;
1337 /* turn off insert mode (if always_insert_mode is not used) */
1338 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1339 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1340 update_title();
1341 return TRUE;
1344 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1345 return FALSE;
1347 if (event->keyval == GDK_Escape) {
1348 g_string_truncate(uzbl.state.keycmd, 0);
1349 update_title();
1350 return TRUE;
1353 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1354 if (event->keyval == GDK_Insert) {
1355 gchar * str;
1356 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1357 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1358 } else {
1359 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1361 if (str) {
1362 g_string_append (uzbl.state.keycmd, str);
1363 update_title ();
1364 free (str);
1366 return TRUE;
1369 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1370 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1371 update_title();
1374 gboolean key_ret = FALSE;
1375 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1376 key_ret = TRUE;
1377 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1379 run_keycmd(key_ret);
1380 update_title();
1381 if (key_ret) return (!uzbl.behave.insert_mode);
1382 return TRUE;
1385 static void
1386 run_keycmd(const gboolean key_ret) {
1387 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1388 Action *action;
1389 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1390 g_string_truncate(uzbl.state.keycmd, 0);
1391 parse_command(action->name, action->param);
1392 return;
1395 /* try if it's an incremental keycmd or one that takes args, and run it */
1396 GString* short_keys = g_string_new ("");
1397 GString* short_keys_inc = g_string_new ("");
1398 unsigned int i;
1399 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1400 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1401 g_string_assign(short_keys_inc, short_keys->str);
1402 g_string_append_c(short_keys, '_');
1403 g_string_append_c(short_keys_inc, '*');
1405 gboolean exec_now = FALSE;
1406 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1407 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1408 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1409 if (key_ret) { /* just quit the incremental command on return */
1410 g_string_truncate(uzbl.state.keycmd, 0);
1411 break;
1412 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1415 if (exec_now) {
1416 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1417 GString* actionname = g_string_new ("");
1418 GString* actionparam = g_string_new ("");
1419 g_string_erase (parampart, 0, i+1);
1420 if (action->name)
1421 g_string_printf (actionname, action->name, parampart->str);
1422 if (action->param)
1423 g_string_printf (actionparam, action->param, parampart->str);
1424 parse_command(actionname->str, actionparam->str);
1425 g_string_free (actionname, TRUE);
1426 g_string_free (actionparam, TRUE);
1427 g_string_free (parampart, TRUE);
1428 if (key_ret)
1429 g_string_truncate(uzbl.state.keycmd, 0);
1430 break;
1433 g_string_truncate(short_keys, short_keys->len - 1);
1435 g_string_free (short_keys, TRUE);
1436 g_string_free (short_keys_inc, TRUE);
1439 static GtkWidget*
1440 create_browser () {
1441 GUI *g = &uzbl.gui;
1443 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1444 //main_window_ref = g_object_ref(scrolled_window);
1445 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
1447 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1448 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1450 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1451 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1452 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1453 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1454 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1455 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1456 g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1457 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1458 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1459 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1461 return scrolled_window;
1464 static GtkWidget*
1465 create_mainbar () {
1466 GUI *g = &uzbl.gui;
1468 g->mainbar = gtk_hbox_new (FALSE, 0);
1470 /* keep a reference to the bar so we can re-pack it at runtime*/
1471 //sbar_ref = g_object_ref(g->mainbar);
1473 g->mainbar_label = gtk_label_new ("");
1474 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1475 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1476 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1477 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1478 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1479 return g->mainbar;
1482 static
1483 GtkWidget* create_window () {
1484 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1485 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1486 gtk_widget_set_name (window, "Uzbl browser");
1487 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1489 return window;
1492 static void
1493 add_binding (const gchar *key, const gchar *act) {
1494 char **parts = g_strsplit(act, " ", 2);
1495 Action *action;
1497 if (!parts)
1498 return;
1500 //Debug:
1501 if (uzbl.state.verbose)
1502 printf ("Binding %-10s : %s\n", key, act);
1503 action = new_action(parts[0], parts[1]);
1505 if(g_hash_table_lookup(uzbl.bindings, key))
1506 g_hash_table_remove(uzbl.bindings, key);
1507 g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1509 g_strfreev(parts);
1512 static gchar*
1513 get_xdg_var (XDG_Var xdg) {
1514 const gchar* actual_value = getenv (xdg.environmental);
1515 const gchar* home = getenv ("HOME");
1517 gchar* return_value = str_replace ("~", home, g_strdup (actual_value));
1519 if (! actual_value || strcmp (actual_value, "") == 0) {
1520 if (xdg.default_value) {
1521 return_value = str_replace ("~", home, g_strdup (xdg.default_value));
1522 } else {
1523 return_value = NULL;
1527 return return_value;
1530 static gchar*
1531 find_xdg_file (int xdg_type, char* filename) {
1532 /* xdg_type = 0 => config
1533 xdg_type = 1 => data
1534 xdg_type = 2 => cache*/
1536 gchar* temporary_file = (char *)malloc (1024);
1537 gchar* temporary_string = NULL;
1538 char* saveptr;
1540 strcpy (temporary_file, get_xdg_var (XDG[xdg_type]));
1542 strcat (temporary_file, filename);
1544 if (! file_exists (temporary_file) && xdg_type != 2) {
1545 temporary_string = (char *) strtok_r (get_xdg_var (XDG[3 + xdg_type]), ":", &saveptr);
1547 while (temporary_string && ! file_exists (temporary_file)) {
1548 strcpy (temporary_file, temporary_string);
1549 strcat (temporary_file, filename);
1550 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1554 if (file_exists (temporary_file)) {
1555 return temporary_file;
1556 } else {
1557 return NULL;
1561 static void
1562 settings_init () {
1563 State *s = &uzbl.state;
1564 Network *n = &uzbl.net;
1566 uzbl.behave.reset_command_mode = 1;
1568 if (!s->config_file) {
1569 s->config_file = g_strdup (find_xdg_file (0, "/uzbl/config"));
1572 if (s->config_file) {
1573 GIOChannel *chan = NULL;
1574 gchar *readbuf = NULL;
1575 gsize len;
1577 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1579 if (chan) {
1580 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1581 == G_IO_STATUS_NORMAL) {
1582 parse_cmd_line(readbuf);
1583 g_free (readbuf);
1586 g_io_channel_unref (chan);
1587 if (uzbl.state.verbose)
1588 printf ("Config %s loaded\n", s->config_file);
1589 } else {
1590 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1592 } else {
1593 if (uzbl.state.verbose)
1594 printf ("No configuration file loaded.\n");
1596 if (!uzbl.behave.status_format)
1597 set_var_value("status_format", STATUS_DEFAULT);
1598 if (!uzbl.behave.title_format_long)
1599 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1600 if (!uzbl.behave.title_format_short)
1601 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1604 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1607 static gchar*
1608 set_useragent(gchar *val) {
1609 if (*val == ' ') {
1610 g_free(val);
1611 return NULL;
1613 gchar *ua = expand_template(val);
1614 if (ua)
1615 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1616 return ua;
1619 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1620 (void) session;
1621 (void) user_data;
1622 if (!uzbl.behave.cookie_handler) return;
1624 gchar * stdout = NULL;
1625 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1626 GString* args = g_string_new ("");
1627 SoupURI * soup_uri = soup_message_get_uri(msg);
1628 g_string_printf (args, "GET %s %s", soup_uri->host, soup_uri->path);
1629 run_command(uzbl.behave.cookie_handler, args->str, TRUE, &stdout);
1630 if(stdout) {
1631 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1633 g_string_free(args, TRUE);
1636 static void
1637 save_cookies (SoupMessage *msg, gpointer user_data){
1638 (void) user_data;
1639 GSList *ck;
1640 char *cookie;
1641 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1642 cookie = soup_cookie_to_set_cookie_header(ck->data);
1643 GString* args = g_string_new ("");
1644 SoupURI * soup_uri = soup_message_get_uri(msg);
1645 g_string_printf (args, "PUT %s %s \"%s\"", soup_uri->host, soup_uri->path, cookie);
1646 run_command(uzbl.behave.cookie_handler, args->str, FALSE, NULL);
1647 g_string_free(args, TRUE);
1648 free(cookie);
1650 g_slist_free(ck);
1655 main (int argc, char* argv[]) {
1656 gtk_init (&argc, &argv);
1657 if (!g_thread_supported ())
1658 g_thread_init (NULL);
1660 strcpy(uzbl.state.executable_path,argv[0]);
1662 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1663 g_option_context_add_main_entries (context, entries, NULL);
1664 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1665 g_option_context_parse (context, &argc, &argv, NULL);
1666 /* initialize hash table */
1667 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1669 uzbl.net.soup_session = webkit_get_default_session();
1670 uzbl.state.keycmd = g_string_new("");
1672 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1673 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1674 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1675 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1677 if(uname(&uzbl.state.unameinfo) == -1)
1678 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1680 setup_regex();
1681 setup_scanner();
1682 commands_hash ();
1683 make_var_to_name_hash();
1686 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1688 uzbl.gui.scrolled_win = create_browser();
1689 create_mainbar();
1691 /* initial packing */
1692 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1693 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1695 uzbl.gui.main_window = create_window ();
1696 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1698 load_uri (uzbl.gui.web_view, uzbl.state.uri); //TODO: is this needed?
1700 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1701 gtk_widget_show_all (uzbl.gui.main_window);
1702 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1704 if (uzbl.state.verbose) {
1705 printf("Uzbl start location: %s\n", argv[0]);
1706 printf("window_id %i\n",(int) uzbl.xwin);
1707 printf("pid %i\n", getpid ());
1708 printf("name: %s\n", uzbl.state.instance_name);
1711 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1712 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1713 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1714 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1715 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1717 settings_init ();
1719 if (!uzbl.behave.show_status)
1720 gtk_widget_hide(uzbl.gui.mainbar);
1721 else
1722 update_title();
1724 create_stdin();
1726 gtk_main ();
1727 clean_up();
1729 return EXIT_SUCCESS;
1732 /* vi: set et ts=4: */