added modkey and always_insert to cmd parser
[uzbl-mobile] / uzbl.c
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.
4
5
6 /*
7  * Copyright (C) 2006, 2007 Apple Inc.
8  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
9  *
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.
18  *
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.
30  */
31
32
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
35
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"
57
58
59 static Uzbl uzbl;
60
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     // Already working commands
67     { "uri",                (void *)&uzbl.state.uri                 },
68     { "status_format",      (void *)&uzbl.behave.status_format      },
69     { "status_background",  (void *)&uzbl.behave.status_background  },
70     { "status_message",     (void *)&uzbl.gui.sbar.msg              },
71     { "show_status",        (void *)&uzbl.behave.show_status        },
72     { "insert_mode",        (void *)&uzbl.behave.insert_mode        },
73     { "modkey"     ,        (void *)&uzbl.behave.modkey             },
74     { "always_insert"     , (void *)&uzbl.behave.always_insert_mode },
75     { "load_finish_handler",(void *)&uzbl.behave.load_finish_handler},
76     { "history_handler",    (void *)&uzbl.behave.history_handler    },
77     { "download_handler",   (void *)&uzbl.behave.download_handler   },
78     { "cookie_handler",     (void *)&uzbl.behave.cookie_handler     },
79     { "fifo_dir",           (void *)&uzbl.behave.fifo_dir           },
80     { "socket_dir",         (void *)&uzbl.behave.socket_dir         },
81     { "proxy_url",          (void *)&uzbl.net.proxy_url             },
82     { "max_conns",          (void *)&uzbl.net.max_conns             },
83     { "max_conns_host",     (void *)&uzbl.net.max_conns_host        },
84     { "http_debug",         (void *)&uzbl.behave.http_debug         },
85     // TODO: write cmd handlers for the following
86     { "useragent",          (void *)&uzbl.net.useragent             },
87     { NULL,                 NULL                                    }
88 }, *n2v_p = var_name_to_ptr;
89
90 /* construct a hash from the var_name_to_ptr array for quick access */
91 static void
92 make_var_to_name_hash() {
93     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
94     while(n2v_p->name) {
95         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, n2v_p->ptr);
96         n2v_p++;
97     }
98 }
99
100 /* commandline arguments (set initial values for the state variables) */
101 static GOptionEntry entries[] =
102 {
103     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,           "Uri to load", "URI" },
104     { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance", "NAME" },
105     { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   "Config file", "FILE" },
106     { NULL,      0, 0, 0, NULL, NULL, NULL }
107 };
108
109 typedef void (*Command)(WebKitWebView*, const char *);
110
111 /* XDG stuff */
112 static char *XDG_CONFIG_HOME_default[256];
113 static char *XDG_CONFIG_DIRS_default = "/etc/xdg";
114
115
116 /* --- UTILITY FUNCTIONS --- */
117
118 char *
119 itos(int val) {
120     char tmp[20];
121
122     snprintf(tmp, sizeof(tmp), "%i", val);
123     return g_strdup(tmp);
124 }
125
126 static char *
127 str_replace (const char* search, const char* replace, const char* string) {
128     return g_strjoinv (replace, g_strsplit(string, search, -1));
129 }
130
131 static sigfunc*
132 setup_signal(int signr, sigfunc *shandler) {
133     struct sigaction nh, oh;
134
135     nh.sa_handler = shandler;
136     sigemptyset(&nh.sa_mask);
137     nh.sa_flags = 0;
138
139     if(sigaction(signr, &nh, &oh) < 0)
140         return SIG_ERR;
141
142     return NULL;
143 }
144
145 static void
146 clean_up(void) {
147     if (uzbl.behave.fifo_dir)
148         unlink (uzbl.comm.fifo_path);
149     if (uzbl.behave.socket_dir)
150         unlink (uzbl.comm.socket_path);
151
152     g_string_free(uzbl.state.keycmd, TRUE);
153     g_hash_table_destroy(uzbl.bindings);
154     g_hash_table_destroy(uzbl.behave.commands);
155 }
156
157
158 /* --- SIGNAL HANDLER --- */
159
160 static void
161 catch_sigterm(int s) {
162     (void) s;
163     clean_up();
164 }
165
166 /* --- CALLBACKS --- */
167
168 static gboolean
169 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
170     (void) web_view;
171     (void) frame;
172     (void) navigation_action;
173     (void) policy_decision;
174     (void) user_data;
175     const gchar* uri = webkit_network_request_get_uri (request);
176     printf("New window requested -> %s \n", uri);
177     new_window_load_uri(uri);
178     return (FALSE);
179 }
180
181 WebKitWebView*
182 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
183     (void) web_view;
184     (void) frame;
185     (void) user_data;
186     if (uzbl.state.selected_url[0]!=0) {
187         printf("\nNew web view -> %s\n",uzbl.state.selected_url);
188         new_window_load_uri(uzbl.state.selected_url);
189     } else {
190         printf("New web view -> %s\n","Nothing to open, exiting");
191     }
192     return (NULL);
193 }
194
195 static gboolean
196 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
197     (void) web_view;
198     (void) user_data;
199     if (uzbl.behave.download_handler) {
200         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
201         printf("Download -> %s\n",uri);
202         run_command_async(uzbl.behave.download_handler, uri);
203     }
204     return (FALSE);
205 }
206
207 /* scroll a bar in a given direction */
208 static void
209 scroll (GtkAdjustment* bar, const char *param) {
210     gdouble amount;
211     gchar *end;
212
213     amount = g_ascii_strtod(param, &end);
214
215     if (*end)
216         fprintf(stderr, "found something after double: %s\n", end);
217
218     gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
219 }
220
221 static void scroll_vert(WebKitWebView* page, const char *param) {
222     (void) page;
223
224     scroll(uzbl.gui.bar_v, param);
225 }
226
227 static void scroll_horz(WebKitWebView* page, const char *param) {
228     (void) page;
229
230     scroll(uzbl.gui.bar_h, param);
231 }
232
233 static void
234 cmd_set_status() {
235     if (!uzbl.behave.show_status) {
236         gtk_widget_hide(uzbl.gui.mainbar);
237     } else {
238         gtk_widget_show(uzbl.gui.mainbar);
239     }
240     update_title();
241 }
242
243 static void
244 toggle_status_cb (WebKitWebView* page, const char *param) {
245     (void)page;
246     (void)param;
247
248     if (uzbl.behave.show_status) {
249         gtk_widget_hide(uzbl.gui.mainbar);
250     } else {
251         gtk_widget_show(uzbl.gui.mainbar);
252     }
253     uzbl.behave.show_status = !uzbl.behave.show_status;
254     update_title();
255 }
256
257 static void
258 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
259     (void) page;
260     (void) title;
261     (void) data;    
262     //ADD HOVER URL TO WINDOW TITLE
263     uzbl.state.selected_url[0] = '\0';
264     if (link) {
265         strcpy (uzbl.state.selected_url, link);
266     }
267     update_title();
268 }
269
270 static void
271 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
272     (void) web_view;
273     (void) web_frame;
274     (void) data;
275     if (uzbl.gui.main_title)
276         g_free (uzbl.gui.main_title);
277     uzbl.gui.main_title = g_strdup (title);
278     update_title();
279 }
280
281 static void
282 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
283     (void) page;
284     (void) data;
285     uzbl.gui.sbar.load_progress = progress;
286     update_title();
287 }
288
289 static void
290 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
291     (void) page;
292     (void) frame;
293     (void) data;
294     if (uzbl.behave.load_finish_handler) {
295         run_command_async(uzbl.behave.load_finish_handler, NULL);
296     }
297 }
298
299 static void
300 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
301     (void) page;
302     (void) data;
303     free (uzbl.state.uri);
304     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
305     uzbl.state.uri = g_string_free (newuri, FALSE);
306     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
307         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
308         update_title();
309     }    
310     g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
311 }
312
313 static void
314 destroy_cb (GtkWidget* widget, gpointer data) {
315     (void) widget;
316     (void) data;
317     gtk_main_quit ();
318 }
319
320 static void
321 log_history_cb () {
322    if (uzbl.behave.history_handler) {
323        time_t rawtime;
324        struct tm * timeinfo;
325        char date [80];
326        time ( &rawtime );
327        timeinfo = localtime ( &rawtime );
328        strftime (date, 80, "%Y-%m-%d %H:%M:%S", timeinfo);
329        GString* args = g_string_new ("");
330        g_string_printf (args, "'%s'", date);
331        run_command_async(uzbl.behave.history_handler, args->str);
332        g_string_free (args, TRUE);
333    }
334 }
335
336
337 /* VIEW funcs (little webkit wrappers) */
338 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
339 VIEWFUNC(reload)
340 VIEWFUNC(reload_bypass_cache)
341 VIEWFUNC(stop_loading)
342 VIEWFUNC(zoom_in)
343 VIEWFUNC(zoom_out)
344 VIEWFUNC(go_back)
345 VIEWFUNC(go_forward)
346 #undef VIEWFUNC
347
348 /* -- command to callback/function map for things we cannot attach to any signals */
349 // TODO: reload
350
351 static struct {char *name; Command command;} cmdlist[] =
352 {
353     { "back",             view_go_back            },
354     { "forward",          view_go_forward         },
355     { "scroll_vert",      scroll_vert             },
356     { "scroll_horz",      scroll_horz             },
357     { "reload",           view_reload,            }, 
358     { "reload_ign_cache", view_reload_bypass_cache},
359     { "stop",             view_stop_loading,      },
360     { "zoom_in",          view_zoom_in,           }, //Can crash (when max zoom reached?).
361     { "zoom_out",         view_zoom_out,          },
362     { "uri",              load_uri                },
363     { "script",           run_js                  },
364     { "toggle_status",    toggle_status_cb        },
365     { "spawn",            spawn                   },
366     { "exit",             close_uzbl              },
367     { "search",           search_text             },
368     { "insert_mode",      set_insert_mode         }
369 };
370
371 static void
372 commands_hash(void)
373 {
374     unsigned int i;
375     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
376
377     for (i = 0; i < LENGTH(cmdlist); i++)
378         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
379 }
380
381 /* -- CORE FUNCTIONS -- */
382
383 void
384 free_action(gpointer act) {
385     Action *action = (Action*)act;
386     g_free(action->name);
387     if (action->param)
388         g_free(action->param);
389     g_free(action);
390 }
391
392 Action*
393 new_action(const gchar *name, const gchar *param) {
394     Action *action = g_new(Action, 1);
395
396     action->name = g_strdup(name);
397     if (param)
398         action->param = g_strdup(param);
399     else
400         action->param = NULL;
401
402     return action;
403 }
404
405 static bool
406 file_exists (const char * filename) {
407     FILE *file = fopen (filename, "r");
408     if (file) {
409         fclose (file);
410         return true;
411     }
412     return false;
413 }
414
415 void
416 set_insert_mode(WebKitWebView *page, const gchar *param) {
417     (void)page;
418     (void)param;
419
420     uzbl.behave.insert_mode = TRUE;
421     update_title();
422 }
423
424 static void
425 load_uri (WebKitWebView * web_view, const gchar *param) {
426     if (param) {
427         GString* newuri = g_string_new (param);
428         if (g_strrstr (param, "://") == NULL)
429             g_string_prepend (newuri, "http://");
430                 /* if we do handle cookies, ask our handler for them */
431         webkit_web_view_load_uri (web_view, newuri->str);
432         g_string_free (newuri, TRUE);
433     }
434 }
435
436 static void
437 run_js (WebKitWebView * web_view, const gchar *param) {
438     if (param)
439         webkit_web_view_execute_script (web_view, param);
440 }
441
442 static void
443 search_text (WebKitWebView *page, const char *param) {
444     if ((param) && (param[0] != '\0')) {
445         strcpy(uzbl.state.searchtx, param);
446     }
447     if (uzbl.state.searchtx[0] != '\0') {
448         printf ("Searching: %s\n", uzbl.state.searchtx);
449         webkit_web_view_unmark_text_matches (page);
450         webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
451         webkit_web_view_set_highlight_text_matches (page, TRUE);
452         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, TRUE, TRUE);
453     }
454 }
455
456 static void
457 new_window_load_uri (const gchar * uri) {
458     GString* to_execute = g_string_new ("");
459     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
460     int i;
461     for (i = 0; entries[i].long_name != NULL; i++) {
462         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0)) {
463             gchar** str = (gchar**)entries[i].arg_data;
464             if (*str!=NULL) {
465                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
466             }
467         }
468     }
469     printf("\n%s\n", to_execute->str);
470     g_spawn_command_line_async (to_execute->str, NULL);
471     g_string_free (to_execute, TRUE);
472 }
473
474 static void
475 close_uzbl (WebKitWebView *page, const char *param) {
476     (void)page;
477     (void)param;
478     gtk_main_quit ();
479 }
480
481 /* --Statusbar functions-- */
482 static char*
483 build_progressbar_ascii(int percent) {
484    int width=10;
485    int i;
486    double l;
487    GString *bar = g_string_new("");
488
489    l = (double)percent*((double)width/100.);
490    l = (int)(l+.5)>=(int)l ? l+.5 : l;
491
492    g_string_append(bar, "[");
493    for(i=0; i<(int)l; i++)
494        g_string_append(bar, "=");
495           
496    for(; i<width; i++)
497        g_string_append(bar, "·");
498    g_string_append(bar, "]");
499
500    return g_string_free(bar, FALSE);
501 }
502
503 static void
504 setup_scanner() {
505      const GScannerConfig scan_config = {
506              (
507               "\t\r\n"
508              )            /* cset_skip_characters */,
509              (
510               G_CSET_a_2_z
511               "_#"
512               G_CSET_A_2_Z
513              )            /* cset_identifier_first */,
514              (
515               G_CSET_a_2_z
516               "_0123456789"
517               G_CSET_A_2_Z
518               G_CSET_LATINS
519               G_CSET_LATINC
520              )            /* cset_identifier_nth */,
521              ( "" )    /* cpair_comment_single */,
522
523              TRUE         /* case_sensitive */,
524
525              FALSE        /* skip_comment_multi */,
526              FALSE        /* skip_comment_single */,
527              FALSE        /* scan_comment_multi */,
528              TRUE         /* scan_identifier */,
529              TRUE         /* scan_identifier_1char */,
530              FALSE        /* scan_identifier_NULL */,
531              TRUE         /* scan_symbols */,
532              FALSE        /* scan_binary */,
533              FALSE        /* scan_octal */,
534              FALSE        /* scan_float */,
535              FALSE        /* scan_hex */,
536              FALSE        /* scan_hex_dollar */,
537              FALSE        /* scan_string_sq */,
538              FALSE        /* scan_string_dq */,
539              TRUE         /* numbers_2_int */,
540              FALSE        /* int_2_float */,
541              FALSE        /* identifier_2_string */,
542              FALSE        /* char_2_token */,
543              FALSE        /* symbol_2_token */,
544              TRUE         /* scope_0_fallback */,
545              FALSE,
546              TRUE
547      };
548
549      uzbl.scan = g_scanner_new(&scan_config);
550      while(symp->symbol_name) {
551          g_scanner_scope_add_symbol(uzbl.scan, 0,
552                          symp->symbol_name,
553                          GINT_TO_POINTER(symp->symbol_token));
554          symp++;
555      }
556 }
557
558 static gchar *
559 parse_status_template(const char *template) {
560      GTokenType token = G_TOKEN_NONE;
561      GString *ret = g_string_new("");
562      gchar *buf=NULL;
563      int sym;
564
565      if(!template)
566          return NULL;
567
568      g_scanner_input_text(uzbl.scan, template, strlen(template));
569      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
570          token = g_scanner_get_next_token(uzbl.scan);
571
572          if(token == G_TOKEN_SYMBOL) {
573              sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
574              switch(sym) {
575                  case SYM_URI:
576                      g_string_append(ret, 
577                          uzbl.state.uri?
578                          g_markup_printf_escaped("%s", uzbl.state.uri):"");
579                      break;
580                  case SYM_LOADPRGS:
581                      g_string_append(ret, itos(uzbl.gui.sbar.load_progress));
582                      break;
583                  case SYM_LOADPRGSBAR:
584                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
585                      g_string_append(ret, buf);
586                      g_free(buf);
587                      break;
588                  case SYM_TITLE:
589                      g_string_append(ret,
590                          uzbl.gui.main_title?
591                          g_markup_printf_escaped("%s", uzbl.gui.main_title):"");
592                      break;
593                  case SYM_NAME:
594                      g_string_append(ret, 
595                          uzbl.state.instance_name?uzbl.state.instance_name:itos(uzbl.xwin));
596                      break;
597                  case SYM_KEYCMD:
598                      g_string_append(ret, 
599                          uzbl.state.keycmd->str ?
600                          g_markup_printf_escaped("%s", uzbl.state.keycmd->str):"");
601                      break;
602                  case SYM_MODE:
603                      g_string_append(ret, 
604                          uzbl.behave.insert_mode?"[I]":"[C]");
605                      break;
606                  case SYM_MSG:
607                      g_string_append(ret, 
608                          uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
609                      break;
610                  default:
611                      break;
612              }
613          }
614          else if(token == G_TOKEN_INT) {
615              g_string_append(ret, itos(g_scanner_cur_value(uzbl.scan).v_int));
616          }
617          else if(token == G_TOKEN_IDENTIFIER) {
618              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
619          }
620          else if(token == G_TOKEN_CHAR) {
621              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
622          }
623      }
624
625      return g_string_free(ret, FALSE);
626 }
627 /* --End Statusbar functions-- */
628
629
630 // make sure to put '' around args, so that if there is whitespace we can still keep arguments together.
631 static gboolean
632 run_command_async(const char *command, const char *args) {
633    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
634     GString* to_execute = g_string_new ("");
635     gboolean result;
636     g_string_printf (to_execute, "%s '%s' '%i' '%i' '%s' '%s'", 
637                     command, uzbl.state.config_file, (int) getpid() ,
638                     (int) uzbl.xwin, uzbl.comm.fifo_path, uzbl.comm.socket_path);
639     g_string_append_printf (to_execute, " '%s' '%s'", 
640                     uzbl.state.uri, "TODO title here");
641     if(args) {
642         g_string_append_printf (to_execute, " %s", args);
643     }
644     result = g_spawn_command_line_async (to_execute->str, NULL);
645     printf("Called %s.  Result: %s\n", to_execute->str, (result ? "TRUE" : "FALSE" ));
646     g_string_free (to_execute, TRUE);
647     return result;
648 }
649
650 static gboolean
651 run_command_sync(const char *command, const char *args, char **stdout) {
652         //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
653     GString* to_execute = g_string_new ("");
654     gboolean result;
655     g_string_printf (to_execute, "%s '%s' '%i' '%i' '%s' '%s'", command, uzbl.state.config_file, (int) getpid() , (int) uzbl.xwin, uzbl.comm.fifo_path, uzbl.comm.socket_path);
656     g_string_append_printf (to_execute, " '%s' '%s'", uzbl.state.uri, "TODO title here");
657     if(args) {
658         g_string_append_printf (to_execute, " %s", args);
659     }
660     result = g_spawn_command_line_sync (to_execute->str, stdout, NULL, NULL, NULL);
661     printf("Called %s.  Result: %s\n", to_execute->str, (result ? "TRUE" : "FALSE" ));
662     g_string_free (to_execute, TRUE);
663     return result;
664 }
665
666 static void
667 spawn(WebKitWebView *web_view, const char *param) {
668     (void)web_view;
669     run_command_async(param, NULL);
670 }
671
672 static void
673 parse_command(const char *cmd, const char *param) {
674     Command c;
675
676     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd)))
677         c(uzbl.gui.web_view, param);
678     else
679         fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
680 }
681
682 /* command parser */
683 static void
684 setup_regex() {
685     GError *err=NULL;
686
687     uzbl.comm.get_regex  = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$", 
688             G_REGEX_OPTIMIZE, 0, &err);
689     uzbl.comm.set_regex  = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
690             G_REGEX_OPTIMIZE, 0, &err);
691     uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$", 
692             G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, &err);
693     uzbl.comm.cmd_regex = g_regex_new("^[Cc][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
694             G_REGEX_OPTIMIZE, 0, &err);
695 }
696
697 static gboolean
698 get_var_value(gchar *name) {
699     void **p = NULL;
700
701     if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
702         if(!strcmp(name, "status_format")) {
703             printf("VAR: %s VALUE: %s\n", name, (char *)*p);
704         } else {
705             printf("VAR: %s VALUE: %d\n", name, (int)*p);
706         }
707     }
708     return TRUE;
709 }
710
711 static void
712 set_proxy_url() {
713     SoupURI *suri;
714
715     if(*uzbl.net.proxy_url == ' '
716        || uzbl.net.proxy_url == NULL) {
717         soup_session_remove_feature_by_type(uzbl.net.soup_session,
718                 (GType) SOUP_SESSION_PROXY_URI);
719     }
720     else {
721         suri = soup_uri_new(uzbl.net.proxy_url);
722         g_object_set(G_OBJECT(uzbl.net.soup_session),
723                 SOUP_SESSION_PROXY_URI,
724                 suri, NULL);
725         soup_uri_free(suri);
726     }
727     return;
728 }
729
730 static void
731 set_modkey() {
732     Behaviour *b = &uzbl.behave;
733
734     if (!b->modkey)
735         b->modkey = "";
736
737     //POSSIBLE MODKEY VALUES (COMBINATIONS CAN BE USED)
738     gchar* modkeyup = g_utf8_strup (b->modkey, -1);
739     if (g_strrstr (modkeyup,"SHIFT") != NULL)    b->modmask |= GDK_SHIFT_MASK;    //the Shift key.
740     if (g_strrstr (modkeyup,"LOCK") != NULL)     b->modmask |= GDK_LOCK_MASK;     //a Lock key (depending on the modifier mapping of the X server this may either be CapsLock or ShiftLock).
741     if (g_strrstr (modkeyup,"CONTROL") != NULL)  b->modmask |= GDK_CONTROL_MASK;  //the Control key.
742     if (g_strrstr (modkeyup,"MOD1") != NULL)     b->modmask |= GDK_MOD1_MASK;     //the fourth modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier, but normally it is the Alt key).
743     if (g_strrstr (modkeyup,"MOD2") != NULL)     b->modmask |= GDK_MOD2_MASK;     //the fifth modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier).
744     if (g_strrstr (modkeyup,"MOD3") != NULL)     b->modmask |= GDK_MOD3_MASK;     //the sixth modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier).
745     if (g_strrstr (modkeyup,"MOD4") != NULL)     b->modmask |= GDK_MOD4_MASK;     //the seventh modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier).
746     if (g_strrstr (modkeyup,"MOD5") != NULL)     b->modmask |= GDK_MOD5_MASK;     //the eighth modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier).
747     if (g_strrstr (modkeyup,"BUTTON1") != NULL)  b->modmask |= GDK_BUTTON1_MASK;  //the first mouse button.
748     if (g_strrstr (modkeyup,"BUTTON2") != NULL)  b->modmask |= GDK_BUTTON2_MASK;  //the second mouse button.
749     if (g_strrstr (modkeyup,"BUTTON3") != NULL)  b->modmask |= GDK_BUTTON3_MASK;  //the third mouse button.
750     if (g_strrstr (modkeyup,"BUTTON4") != NULL)  b->modmask |= GDK_BUTTON4_MASK;  //the fourth mouse button.
751     if (g_strrstr (modkeyup,"BUTTON5") != NULL)  b->modmask |= GDK_BUTTON5_MASK;  //the fifth mouse button.
752     if (g_strrstr (modkeyup,"SUPER") != NULL)    b->modmask |= GDK_SUPER_MASK;    //the Super modifier. Since 2.10
753     if (g_strrstr (modkeyup,"HYPER") != NULL)    b->modmask |= GDK_HYPER_MASK;    //the Hyper modifier. Since 2.10
754     if (g_strrstr (modkeyup,"META") != NULL)     b->modmask |= GDK_META_MASK;     //the Meta modifier. Since 2.10  */
755     g_free (modkeyup);
756 }
757
758 static gboolean
759 var_is(const char *x, const char *y) {
760     gboolean ret = FALSE;
761
762     if(!strcmp(x, y))
763         ret = TRUE;
764
765     return ret;
766 }
767
768 static gboolean
769 set_var_value(gchar *name, gchar *val) {
770     void **p = NULL;
771     char *endp = NULL;
772
773     if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
774         if(var_is("status_message", name)
775            || var_is("status_background", name)
776            || var_is("status_format", name)
777            || var_is("load_finish_handler", name)
778            || var_is("history_handler", name)
779            || var_is("download_handler", name)
780            || var_is("cookie_handler", name)) {
781             if(*p)
782                 free(*p);
783             *p = g_strdup(val);
784             update_title();
785         } 
786         else if(var_is("uri", name)) {
787             if(*p) free(*p);
788             *p = g_strdup(val);
789             load_uri(uzbl.gui.web_view, (const gchar*)*p);
790         } 
791         else if(var_is("proxy_url", name)) {
792             if(*p) free(*p);
793             *p = g_strdup(val);
794             set_proxy_url();
795         }
796         else if(var_is("fifo_dir", name)) {
797             if(*p) free(*p);
798             *p = init_fifo(g_strdup(val));
799         }
800         else if(var_is("socket_dir", name)) {
801             if(*p) free(*p);
802             *p = init_socket(g_strdup(val));
803         }
804         else if(var_is("modkey", name)) {
805             if(*p) free(*p);
806             *p = g_strdup(val);
807             set_modkey();
808         }
809         /* variables that take int values */
810         else {
811             int *ip = p;
812             *ip = (int)strtoul(val, &endp, 10);
813
814             if(var_is("show_status", name)) {
815                 cmd_set_status();
816             }
817             else if(var_is("always_insert", name)) {
818
819                 uzbl.behave.insert_mode = 
820                     uzbl.behave.always_insert_mode ?  TRUE : FALSE;
821                 update_title();
822             }
823             else if (var_is("max_conns", name)) {
824                 g_object_set(G_OBJECT(uzbl.net.soup_session),
825                              SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
826             }
827             else if (var_is("max_conns_host", name)) {
828                 g_object_set(G_OBJECT(uzbl.net.soup_session),
829                              SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
830             }
831             else if (var_is("http_debug", name)) {
832                 //soup_session_remove_feature
833                 //    (uzbl.net.soup_session, uzbl.net.soup_logger);
834                 soup_session_remove_feature
835                     (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
836                 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
837                 /*g_free(uzbl.net.soup_logger);*/
838                   
839                 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
840                 soup_session_add_feature(uzbl.net.soup_session,
841                                          SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
842             }
843         }
844     }
845     return TRUE;
846 }
847
848 static void 
849 parse_cmd_line(char *ctl_line) {
850     gchar **tokens;
851
852     /* SET command */
853     if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
854         tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
855         if(tokens[0][0] == 0) {
856             set_var_value(tokens[1], tokens[2]);
857             g_strfreev(tokens);
858         }
859         else 
860             printf("Error in command: %s\n", tokens[0]);
861     }
862     /* GET command */
863     else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
864         tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
865         if(tokens[0][0] == 0) {
866             get_var_value(tokens[1]);
867             g_strfreev(tokens);
868         }
869         else 
870             printf("Error in command: %s\n", tokens[0]);
871     } 
872     /* BIND command */
873     else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
874         tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
875         if(tokens[0][0] == 0) {
876             add_binding(tokens[1], tokens[2]);
877             g_strfreev(tokens);
878         }
879         else 
880             printf("Error in command: %s\n", tokens[0]);
881     }
882     /* CMD command */
883     else if(ctl_line[0] == 'C' || ctl_line[0] == 'c') {
884         tokens = g_regex_split(uzbl.comm.cmd_regex, ctl_line, 0);
885         if(tokens[0][0] == 0) {
886             parse_command(tokens[1], tokens[2]);
887             g_strfreev(tokens);
888         }
889         else
890             printf("Error in command: %s\n", tokens[0]);
891     }
892     /* Comments */
893     else if(   (ctl_line[0] == '#')
894             || (ctl_line[0] == ' ')
895             || (ctl_line[0] == '\n'))
896         ; /* ignore these lines */
897     else
898         printf("Command not understood (%s)\n", ctl_line);
899
900     return;
901 }
902
903 static gchar*
904 build_stream_name(int type, const gchar* dir) {
905     char *xwin_str;
906     State *s = &uzbl.state;
907     gchar *str;
908
909     xwin_str = itos((int)uzbl.xwin);
910     if (type == FIFO) {
911         str = g_strdup_printf
912             ("%s/uzbl_fifo_%s", dir,
913              s->instance_name ? s->instance_name : xwin_str);
914     } else if (type == SOCKET) {
915         str = g_strdup_printf
916             ("%s/uzbl_socket_%s", dir,
917              s->instance_name ? s->instance_name : xwin_str );
918     }
919     g_free(xwin_str);
920     return str;
921 }
922
923 static void
924 control_fifo(GIOChannel *gio, GIOCondition condition) {
925     printf("triggered\n");
926     gchar *ctl_line;
927     GIOStatus ret;
928     GError *err = NULL;
929
930     if (condition & G_IO_HUP)
931         g_error ("Fifo: Read end of pipe died!\n");
932
933     if(!gio)
934        g_error ("Fifo: GIOChannel broke\n");
935
936     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
937     if (ret == G_IO_STATUS_ERROR)
938         g_error ("Fifo: Error reading: %s\n", err->message);
939
940     parse_cmd_line(ctl_line);
941     g_free(ctl_line);
942
943     return;
944 }
945
946 static gchar*
947 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
948     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
949         if (unlink(uzbl.comm.fifo_path) == -1)
950             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
951         g_free(uzbl.comm.fifo_path);
952         uzbl.comm.fifo_path = NULL;
953     }
954
955     if (!strcmp(dir, " ")) { /* space unsets the variable */
956         g_free(dir);
957         return NULL;
958     }
959
960     GIOChannel *chan = NULL;
961     GError *error = NULL;
962     gchar *path = build_stream_name(FIFO, dir);
963     
964     if (!file_exists(path)) {
965         if (mkfifo (path, 0666) == 0) {
966             // 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.            
967             chan = g_io_channel_new_file(path, "r+", &error);
968             if (chan) {
969                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
970                     printf ("init_fifo: created successfully as %s\n", path);
971                     uzbl.comm.fifo_path = path;
972                     return dir;
973                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
974             } else g_warning ("init_fifo: can't open: %s\n", error->message);
975         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
976     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
977
978     /* if we got this far, there was an error; cleanup */
979     g_free(path);
980     g_free(dir);
981     return NULL;
982 }
983
984 static gboolean
985 control_stdin(GIOChannel *gio, GIOCondition condition) {
986     gchar *ctl_line = NULL;
987     gsize ctl_line_len = 0;
988     GIOStatus ret;
989     GError *err = NULL;
990
991     if (condition & G_IO_HUP) { 
992         ret = g_io_channel_shutdown (gio, FALSE, &err);
993         return FALSE;
994     }
995
996     ret = g_io_channel_read_line(gio, &ctl_line, &ctl_line_len, NULL, &err);
997     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
998         return FALSE;
999
1000     parse_cmd_line(ctl_line);
1001     g_free(ctl_line);
1002
1003     return TRUE;
1004 }
1005
1006 static void
1007 create_stdin () {
1008     GIOChannel *chan = NULL;
1009     GError *error = NULL;
1010
1011     chan = g_io_channel_unix_new(fileno(stdin));
1012     if (chan) {
1013         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1014             g_error ("Stdin: could not add watch\n");
1015         } else {
1016             printf ("Stdin: watch added successfully\n");
1017         }
1018     } else {
1019         g_error ("Stdin: Error while opening: %s\n", error->message);
1020     }
1021 }
1022
1023 static void
1024 control_socket(GIOChannel *chan) {
1025     struct sockaddr_un remote;
1026     char buffer[512], *ctl_line;
1027     char temp[128];
1028     int sock, clientsock, n, done;
1029     unsigned int t;
1030
1031     sock = g_io_channel_unix_get_fd(chan);
1032
1033     memset (buffer, 0, sizeof (buffer));
1034
1035     t          = sizeof (remote);
1036     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1037
1038     done = 0;
1039     do {
1040         memset (temp, 0, sizeof (temp));
1041         n = recv (clientsock, temp, 128, 0);
1042         if (n == 0) {
1043             buffer[strlen (buffer)] = '\0';
1044             done = 1;
1045         }
1046         if (!done)
1047             strcat (buffer, temp);
1048     } while (!done);
1049
1050     if (strcmp (buffer, "\n") < 0) {
1051         buffer[strlen (buffer) - 1] = '\0';
1052     } else {
1053         buffer[strlen (buffer)] = '\0';
1054     }
1055     close (clientsock);
1056     ctl_line = g_strdup(buffer);
1057     parse_cmd_line (ctl_line);
1058
1059 /*
1060    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1061     GError *error = NULL;
1062     gsize len;
1063     GIOStatus ret;
1064     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1065     if (ret == G_IO_STATUS_ERROR)
1066         g_error ("Error reading: %s\n", error->message);
1067
1068     printf("Got line %s (%u bytes) \n",ctl_line, len);
1069     if(ctl_line) {
1070        parse_line(ctl_line);
1071 */
1072
1073     g_free(ctl_line);
1074     return;
1075 }
1076
1077 static gchar*
1078 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1079     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1080         if (unlink(uzbl.comm.socket_path) == -1)
1081             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1082         g_free(uzbl.comm.socket_path);
1083         uzbl.comm.socket_path = NULL;
1084     }
1085     
1086     if (!strcmp(dir, " ")) {
1087         g_free(dir);
1088         return NULL;
1089     }
1090
1091     GIOChannel *chan = NULL;
1092     int sock, len;
1093     struct sockaddr_un local;
1094     gchar *path = build_stream_name(SOCKET, dir);
1095     
1096     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1097
1098     local.sun_family = AF_UNIX;
1099     strcpy (local.sun_path, path);
1100     unlink (local.sun_path);
1101
1102     len = strlen (local.sun_path) + sizeof (local.sun_family);
1103     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1104         printf ("init_socket: opened in %s\n", path);
1105         listen (sock, 5);
1106
1107         if( (chan = g_io_channel_unix_new(sock)) ) {
1108             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1109             uzbl.comm.socket_path = path;
1110             return dir;
1111         }
1112     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1113
1114     /* if we got this far, there was an error; cleanup */
1115     g_free(path);
1116     g_free(dir);
1117     return NULL;
1118 }
1119
1120 static void
1121 update_title (void) {
1122     GString* string_long = g_string_new ("");
1123     GString* string_short = g_string_new ("");
1124     char* iname = NULL;
1125     gchar *statln;
1126     int iname_len;
1127     State *s = &uzbl.state;
1128     Behaviour *b = &uzbl.behave;
1129
1130     if(s->instance_name) {
1131             iname_len = strlen(s->instance_name)+4;
1132             iname = malloc(iname_len);
1133             snprintf(iname, iname_len, "<%s> ", s->instance_name);
1134             
1135             g_string_prepend(string_long, iname);
1136             g_string_prepend(string_short, iname);
1137             free(iname);
1138     }
1139
1140     g_string_append_printf(string_long, "%s ", s->keycmd->str);
1141     if (!b->always_insert_mode)
1142         g_string_append (string_long, (b->insert_mode ? "[I] " : "[C] "));
1143     if (uzbl.gui.main_title) {
1144         g_string_append (string_long, uzbl.gui.main_title);
1145         g_string_append (string_short, uzbl.gui.main_title);
1146     }
1147     g_string_append (string_long, " - Uzbl browser");
1148     g_string_append (string_short, " - Uzbl browser");
1149     if (s->selected_url[0]!=0) {
1150         g_string_append_printf (string_long, " -> (%s)", s->selected_url);
1151     }
1152
1153     gchar* title_long = g_string_free (string_long, FALSE);
1154     gchar* title_short = g_string_free (string_short, FALSE);
1155
1156     if (b->show_status) {
1157         gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), title_short);
1158         // TODO: we should probably not do this every time we want to update the title..?
1159         statln = parse_status_template(uzbl.behave.status_format);
1160         gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), statln);
1161         if (b->status_background) {
1162             GdkColor color;
1163             gdk_color_parse (b->status_background, &color);
1164             //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)
1165             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1166         }
1167         g_free(statln);
1168     } else {
1169         gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), title_long);
1170     }
1171
1172     g_free (title_long);
1173     g_free (title_short);
1174 }
1175
1176 static gboolean
1177 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1178 {
1179     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1180
1181     (void) page;
1182     Action *action;
1183
1184     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1185         || 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)
1186         return FALSE;
1187
1188     /* turn off insert mode (if always_insert_mode is not used) */
1189     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1190         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1191         update_title();
1192         return TRUE;
1193     }
1194
1195     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1196         return FALSE;
1197
1198     if (event->keyval == GDK_Escape) {
1199         g_string_truncate(uzbl.state.keycmd, 0);
1200         update_title();
1201         return TRUE;
1202     }
1203
1204     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1205     if (event->keyval == GDK_Insert) {
1206         gchar * str;
1207         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1208             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1209         } else {
1210             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)); 
1211         }
1212         if (str) {
1213             g_string_append_printf (uzbl.state.keycmd, "%s",  str);
1214             update_title ();
1215             free (str);
1216         }
1217         return TRUE;
1218     }
1219
1220     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1221         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1222         update_title();
1223     }
1224
1225     gboolean key_ret = FALSE;
1226     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1227         key_ret = TRUE;
1228
1229     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1230     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1231         g_string_truncate(uzbl.state.keycmd, 0);
1232         parse_command(action->name, action->param);
1233     }
1234
1235     GString* short_keys = g_string_new ("");
1236     GString* short_keys_inc = g_string_new ("");
1237     unsigned int i;
1238     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1239         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1240         g_string_assign(short_keys_inc, short_keys->str);
1241         g_string_append_c(short_keys, '_');
1242         g_string_append_c(short_keys_inc, '*');
1243             
1244         gboolean exec_now = FALSE;
1245         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1246             if (key_ret) exec_now = TRUE; // run normal cmds only if return was pressed
1247         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1248             if (key_ret) { // just quit the incremental command on return
1249                 g_string_truncate(uzbl.state.keycmd, 0);
1250                 break;
1251             } else exec_now = TRUE; // always exec inc. commands on keys other than return
1252         }
1253
1254         if (exec_now) {
1255             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1256             GString* actionname = g_string_new ("");
1257             GString* actionparam = g_string_new ("");
1258             g_string_erase (parampart, 0, i+1);
1259             if (action->name)
1260                 g_string_printf (actionname, action->name, parampart->str);
1261             if (action->param)
1262                 g_string_printf (actionparam, action->param, parampart->str);
1263             parse_command(actionname->str, actionparam->str);
1264             g_string_free (actionname, TRUE);
1265             g_string_free (actionparam, TRUE);
1266             g_string_free (parampart, TRUE);
1267             if (key_ret)
1268                 g_string_truncate(uzbl.state.keycmd, 0);
1269             break;
1270         }      
1271         
1272         g_string_truncate(short_keys, short_keys->len - 1);
1273     }
1274     g_string_free (short_keys, TRUE);
1275     g_string_free (short_keys_inc, TRUE);
1276     update_title();
1277     if (key_ret) return (!uzbl.behave.insert_mode);
1278     return TRUE;
1279 }
1280
1281 static GtkWidget*
1282 create_browser () {
1283     GUI *g = &uzbl.gui;
1284
1285     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1286     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
1287
1288     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1289     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1290
1291     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1292     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1293     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1294     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (log_history_cb), g->web_view);
1295     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1296     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1297     g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1298     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view); 
1299     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view); 
1300     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);  
1301
1302     return scrolled_window;
1303 }
1304
1305 static GtkWidget*
1306 create_mainbar () {
1307     GUI *g = &uzbl.gui;
1308
1309     g->mainbar = gtk_hbox_new (FALSE, 0);
1310     g->mainbar_label = gtk_label_new ("");  
1311     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1312     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1313     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1314     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1315     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1316     return g->mainbar;
1317 }
1318
1319 static
1320 GtkWidget* create_window () {
1321     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1322     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1323     gtk_widget_set_name (window, "Uzbl browser");
1324     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1325
1326     return window;
1327 }
1328
1329 static void
1330 add_binding (const gchar *key, const gchar *act) {
1331     char **parts = g_strsplit(act, " ", 2);
1332     Action *action;
1333
1334     if (!parts)
1335         return;
1336
1337     //Debug:
1338     printf ("Binding %-10s : %s\n", key, act);
1339     action = new_action(parts[0], parts[1]);
1340     
1341     if(g_hash_table_lookup(uzbl.bindings, key))
1342         g_hash_table_remove(uzbl.bindings, key);
1343     g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1344
1345     g_strfreev(parts);
1346 }
1347
1348 static void
1349 settings_init () {
1350     GKeyFile* config = NULL;
1351     gboolean res  = FALSE;
1352     char *saveptr;
1353     State *s = &uzbl.state;
1354     Network *n = &uzbl.net;
1355     Behaviour *b = &uzbl.behave;
1356
1357     if (!s->config_file) {
1358         const char* XDG_CONFIG_HOME = getenv ("XDG_CONFIG_HOME");
1359         if (! XDG_CONFIG_HOME || ! strcmp (XDG_CONFIG_HOME, "")) {
1360           XDG_CONFIG_HOME = (char*)XDG_CONFIG_HOME_default;
1361         }
1362         printf("XDG_CONFIG_HOME: %s\n", XDG_CONFIG_HOME);
1363     
1364         strcpy (s->config_file_path, XDG_CONFIG_HOME);
1365         strcat (s->config_file_path, "/uzbl/config");
1366         if (file_exists (s->config_file_path)) {
1367           printf ("Config file %s found.\n", s->config_file_path);
1368           s->config_file = &s->config_file_path[0];
1369         } else {
1370             // Now we check $XDG_CONFIG_DIRS
1371             char *XDG_CONFIG_DIRS = getenv ("XDG_CONFIG_DIRS");
1372             if (! XDG_CONFIG_DIRS || ! strcmp (XDG_CONFIG_DIRS, ""))
1373                 XDG_CONFIG_DIRS = XDG_CONFIG_DIRS_default;
1374
1375             printf("XDG_CONFIG_DIRS: %s\n", XDG_CONFIG_DIRS);
1376
1377             char buffer[512];
1378             strcpy (buffer, XDG_CONFIG_DIRS);
1379             const gchar* dir = (char *) strtok_r (buffer, ":", &saveptr);
1380             while (dir && ! file_exists (s->config_file_path)) {
1381                 strcpy (s->config_file_path, dir);
1382                 strcat (s->config_file_path, "/uzbl/config_file_pathig");
1383                 if (file_exists (s->config_file_path)) {
1384                     printf ("Config file %s found.\n", s->config_file_path);
1385                     s->config_file = &s->config_file_path[0];
1386                 }
1387                 dir = (char * ) strtok_r (NULL, ":", &saveptr);
1388             }
1389         }
1390     }
1391
1392     if (s->config_file) {
1393         config = g_key_file_new ();
1394         res = g_key_file_load_from_file (config, s->config_file, G_KEY_FILE_NONE, NULL);
1395           if (res) {
1396             printf ("Config %s loaded\n", s->config_file);
1397           } else {
1398             fprintf (stderr, "Config %s loading failed\n", s->config_file);
1399         }
1400     } else {
1401         printf ("No configuration.\n");
1402     }
1403
1404     if (res) {
1405         b->status_top         = g_key_file_get_boolean (config, "behavior", "status_top",         NULL);
1406         b->reset_command_mode = g_key_file_get_boolean (config, "behavior", "reset_command_mode", NULL);
1407     }
1408
1409     printf ("Reset mode: %s\n"      ,   (b->reset_command_mode ? "TRUE"              : "FALSE"));
1410
1411     /* networking options */
1412     if (res) {
1413         n->useragent      = g_key_file_get_value   (config, "network", "user-agent",         NULL);
1414     }
1415
1416     if(n->useragent){
1417         char* newagent  = malloc(1024);
1418
1419         strcpy(newagent, str_replace("%webkit-major%", itos(WEBKIT_MAJOR_VERSION), n->useragent));
1420         strcpy(newagent, str_replace("%webkit-minor%", itos(WEBKIT_MINOR_VERSION), newagent));
1421         strcpy(newagent, str_replace("%webkit-micro%", itos(WEBKIT_MICRO_VERSION), newagent));
1422
1423         if (uname (&s->unameinfo) == -1) {
1424             printf("Error getting uname info. Not replacing system-related user agent variables.\n");
1425         } else {
1426             strcpy(newagent, str_replace("%sysname%",     s->unameinfo.sysname, newagent));
1427             strcpy(newagent, str_replace("%nodename%",    s->unameinfo.nodename, newagent));
1428             strcpy(newagent, str_replace("%kernrel%",     s->unameinfo.release, newagent));
1429             strcpy(newagent, str_replace("%kernver%",     s->unameinfo.version, newagent));
1430             strcpy(newagent, str_replace("%arch-system%", s->unameinfo.machine, newagent));
1431
1432             #ifdef _GNU_SOURCE
1433                 strcpy(newagent, str_replace("%domainname%", s->unameinfo.domainname, newagent));
1434             #endif
1435         }
1436
1437         strcpy(newagent, str_replace("%arch-uzbl%",    ARCH,                       newagent));
1438         strcpy(newagent, str_replace("%commit%",       COMMIT,                     newagent));
1439
1440         n->useragent = malloc(1024);
1441         strcpy(n->useragent, newagent);
1442         g_object_set(G_OBJECT(n->soup_session), SOUP_SESSION_USER_AGENT, n->useragent, NULL);
1443     }
1444
1445     printf("User-agent: %s\n", n->useragent? n->useragent : "default");
1446
1447     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1448 }
1449
1450 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1451     (void) session;
1452     (void) user_data;
1453     if (!uzbl.behave.cookie_handler) return;
1454     
1455     gchar * stdout = NULL;
1456     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1457     GString* args = g_string_new ("");
1458     SoupURI * soup_uri = soup_message_get_uri(msg);
1459     g_string_printf (args, "GET %s %s", soup_uri->host, soup_uri->path);
1460     run_command_sync(uzbl.behave.cookie_handler, args->str, &stdout);
1461     if(stdout) {
1462         soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1463     }
1464     g_string_free(args, TRUE);
1465 }
1466
1467 static void
1468 save_cookies (SoupMessage *msg, gpointer user_data){
1469     (void) user_data;
1470     GSList *ck;
1471     char *cookie;
1472     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1473         cookie = soup_cookie_to_set_cookie_header(ck->data);
1474         GString* args = g_string_new ("");
1475         SoupURI * soup_uri = soup_message_get_uri(msg);
1476         g_string_printf (args, "PUT %s %s \"%s\"", soup_uri->host, soup_uri->path, cookie);
1477         run_command_async(uzbl.behave.cookie_handler, args->str);
1478         g_string_free(args, TRUE);
1479         free(cookie);
1480     }
1481     g_slist_free(ck);
1482 }
1483
1484 int
1485 main (int argc, char* argv[]) {
1486     gtk_init (&argc, &argv);
1487     if (!g_thread_supported ())
1488         g_thread_init (NULL);
1489
1490     printf("Uzbl start location: %s\n", argv[0]);
1491     strcpy(uzbl.state.executable_path,argv[0]);
1492
1493     strcat ((char *) XDG_CONFIG_HOME_default, getenv ("HOME"));
1494     strcat ((char *) XDG_CONFIG_HOME_default, "/.config");
1495
1496     GError *error = NULL;
1497     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1498     g_option_context_add_main_entries (context, entries, NULL);
1499     g_option_context_add_group (context, gtk_get_option_group (TRUE));
1500     g_option_context_parse (context, &argc, &argv, &error);
1501     /* initialize hash table */
1502     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1503         
1504     uzbl.net.soup_session = webkit_get_default_session();
1505     uzbl.state.keycmd = g_string_new("");
1506
1507     settings_init ();
1508     commands_hash ();
1509         
1510
1511     GtkWidget* vbox = gtk_vbox_new (FALSE, 0);
1512     if (uzbl.behave.status_top)
1513         gtk_box_pack_start (GTK_BOX (vbox), create_mainbar (), FALSE, TRUE, 0);
1514     gtk_box_pack_start (GTK_BOX (vbox), create_browser (), TRUE, TRUE, 0);
1515     if (!uzbl.behave.status_top)
1516         gtk_box_pack_start (GTK_BOX (vbox), create_mainbar (), FALSE, TRUE, 0);
1517
1518     uzbl.gui.main_window = create_window ();
1519     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), vbox);
1520
1521     load_uri (uzbl.gui.web_view, uzbl.state.uri);
1522
1523     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1524     gtk_widget_show_all (uzbl.gui.main_window);
1525     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1526     printf("window_id %i\n",(int) uzbl.xwin);
1527     printf("pid %i\n", getpid ());
1528     printf("name: %s\n", uzbl.state.instance_name);
1529
1530     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1531     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1532     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1533     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1534     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1535
1536
1537     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1538         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1539
1540
1541     setup_regex();
1542     setup_scanner();
1543
1544     if (!uzbl.behave.status_format)
1545         uzbl.behave.status_format = g_strdup(STATUS_DEFAULT);
1546     if (!uzbl.behave.show_status)
1547         gtk_widget_hide(uzbl.gui.mainbar);
1548     else
1549         update_title();
1550
1551
1552     make_var_to_name_hash();
1553     create_stdin();
1554     /*if (uzbl.behave.fifo_dir)
1555       init_fifo ();*/
1556     /*if (uzbl.behave.socket_dir)
1557       init_socket ();*/
1558
1559     gtk_main ();
1560     clean_up();
1561
1562     return EXIT_SUCCESS;
1563 }
1564
1565 /* vi: set et ts=4: */