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