support for some default settings so that uzbl is more uzbl by default (without confi...
[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 #include "config.h"
58
59 static Uzbl uzbl;
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
61
62 /* commandline arguments (set initial values for the state variables) */
63 static GOptionEntry entries[] =
64 {
65     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,           "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
66     { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,       "Whether to print all messages or just errors.", NULL },
67     { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
68     { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
69     { NULL,      0, 0, 0, NULL, NULL, NULL }
70 };
71
72
73 /* associate command names to their properties */
74 typedef const struct {
75     void **ptr;
76     int type;
77     void (*func)(void);
78 } uzbl_cmdprop;
79
80 enum {TYPE_INT, TYPE_STRING};
81
82 const struct {
83     char *name;
84     uzbl_cmdprop cp;
85 } var_name_to_ptr[] = {
86 /*    variable name           pointer to variable in code                         variable type      callback function  */
87 /*  ------------------------------------------------------------------------------------------------------------------- */
88     { "uri",                {.ptr = (void *)&uzbl.state.uri,                   .type = TYPE_STRING, .func = cmd_load_uri}},
89     { "status_message",     {.ptr = (void *)&uzbl.gui.sbar.msg,                .type = TYPE_STRING, .func = update_title}},
90     { "show_status",        {.ptr = (void *)&uzbl.behave.show_status,          .type = TYPE_INT,    .func = cmd_set_status}},
91     { "status_top",         {.ptr = (void *)&uzbl.behave.status_top,           .type = TYPE_INT,    .func = move_statusbar}},
92     { "status_format",      {.ptr = (void *)&uzbl.behave.status_format,        .type = TYPE_STRING, .func = update_title}},
93     { "status_pbar_done",   {.ptr = (void *)&uzbl.gui.sbar.progress_s,         .type = TYPE_STRING, .func = update_title}},
94     { "status_pbar_pending",{.ptr = (void *)&uzbl.gui.sbar.progress_u,         .type = TYPE_STRING, .func = update_title}},
95     { "status_pbar_width",  {.ptr = (void *)&uzbl.gui.sbar.progress_w,         .type = TYPE_INT,    .func = update_title}},
96     { "status_background",  {.ptr = (void *)&uzbl.behave.status_background,    .type = TYPE_STRING, .func = update_title}},
97     { "title_format_long",  {.ptr = (void *)&uzbl.behave.title_format_long,    .type = TYPE_STRING, .func = update_title}},
98     { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short,   .type = TYPE_STRING, .func = update_title}},
99     { "insert_mode",        {.ptr = (void *)&uzbl.behave.insert_mode,          .type = TYPE_INT,    .func = NULL}},
100     { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode,   .type = TYPE_INT,    .func = cmd_always_insert_mode}},
101     { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode,   .type = TYPE_INT,    .func = NULL}},
102     { "modkey",             {.ptr = (void *)&uzbl.behave.modkey,               .type = TYPE_STRING, .func = cmd_modkey}},
103     { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler,  .type = TYPE_STRING, .func = NULL}},
104     { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler,   .type = TYPE_STRING, .func = NULL}},
105     { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler,  .type = TYPE_STRING, .func = NULL}},
106     { "history_handler",    {.ptr = (void *)&uzbl.behave.history_handler,      .type = TYPE_STRING, .func = NULL}},
107     { "download_handler",   {.ptr = (void *)&uzbl.behave.download_handler,     .type = TYPE_STRING, .func = NULL}},
108     { "cookie_handler",     {.ptr = (void *)&uzbl.behave.cookie_handler,       .type = TYPE_STRING, .func = NULL}},
109     { "fifo_dir",           {.ptr = (void *)&uzbl.behave.fifo_dir,             .type = TYPE_STRING, .func = cmd_fifo_dir}},
110     { "socket_dir",         {.ptr = (void *)&uzbl.behave.socket_dir,           .type = TYPE_STRING, .func = cmd_socket_dir}},
111     { "http_debug",         {.ptr = (void *)&uzbl.behave.http_debug,           .type = TYPE_INT,    .func = cmd_http_debug}},
112     { "default_font_size",  {.ptr = (void *)&uzbl.behave.default_font_size,    .type = TYPE_INT,    .func = cmd_default_font_size}},
113     { "minimum_font_size",  {.ptr = (void *)&uzbl.behave.minimum_font_size,    .type = TYPE_INT,    .func = cmd_minimum_font_size}},
114     { "shell_cmd",          {.ptr = (void *)&uzbl.behave.shell_cmd,            .type = TYPE_STRING, .func = NULL}},
115     { "proxy_url",          {.ptr = (void *)&uzbl.net.proxy_url,               .type = TYPE_STRING, .func = set_proxy_url}},
116     { "max_conns",          {.ptr = (void *)&uzbl.net.max_conns,               .type = TYPE_INT,    .func = cmd_max_conns}},
117     { "max_conns_host",     {.ptr = (void *)&uzbl.net.max_conns_host,          .type = TYPE_INT,    .func = cmd_max_conns_host}},
118     { "useragent",          {.ptr = (void *)&uzbl.net.useragent,               .type = TYPE_STRING, .func = cmd_useragent}},
119     { NULL,                 {.ptr = NULL,                                      .type = TYPE_INT,    .func = NULL}}
120 }, *n2v_p = var_name_to_ptr;
121
122 const struct {
123     char *key;
124     guint mask;
125 } modkeys[] = {
126     { "SHIFT",   GDK_SHIFT_MASK   }, // shift
127     { "LOCK",    GDK_LOCK_MASK    }, // capslock or shiftlock, depending on xserver's modmappings
128     { "CONTROL", GDK_CONTROL_MASK }, // control
129     { "MOD1",    GDK_MOD1_MASK    }, // 4th mod - normally alt but depends on modmappings
130     { "MOD2",    GDK_MOD2_MASK    }, // 5th mod
131     { "MOD3",    GDK_MOD3_MASK    }, // 6th mod
132     { "MOD4",    GDK_MOD4_MASK    }, // 7th mod
133     { "MOD5",    GDK_MOD5_MASK    }, // 8th mod
134     { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
135     { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
136     { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
137     { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
138     { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
139     { "SUPER",   GDK_SUPER_MASK   }, // super (since 2.10)
140     { "HYPER",   GDK_HYPER_MASK   }, // hyper (since 2.10)
141     { "META",    GDK_META_MASK    }, // meta (since 2.10)
142     { NULL,      0                }
143 };
144
145
146 /* construct a hash from the var_name_to_ptr array for quick access */
147 static void
148 make_var_to_name_hash() {
149     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
150     while(n2v_p->name) {
151         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
152         n2v_p++;
153     }
154 }
155
156
157 /* --- UTILITY FUNCTIONS --- */
158
159 char *
160 itos(int val) {
161     char tmp[20];
162
163     snprintf(tmp, sizeof(tmp), "%i", val);
164     return g_strdup(tmp);
165 }
166
167 static gchar*
168 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
169
170 static char *
171 str_replace (const char* search, const char* replace, const char* string) {
172     gchar **buf;
173     char *ret;
174
175     buf = g_strsplit (string, search, -1);
176     ret = g_strjoinv (replace, buf);
177     //g_strfreev(buf); - segfaults.
178
179     return ret;
180 }
181
182 static GArray*
183 read_file_by_line (gchar *path) {
184     GIOChannel *chan = NULL;
185     gchar *readbuf = NULL;
186     gsize len;
187     GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
188     int i = 0;
189     
190     chan = g_io_channel_new_file(path, "r", NULL);
191     
192     if (chan) {
193         while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
194             const gchar* val = g_strdup (readbuf);
195             g_array_append_val (lines, val);
196             g_free (readbuf);
197             i ++;
198         }
199         
200         g_io_channel_unref (chan);
201     } else {
202         fprintf(stderr, "File '%s' not be read.\n", path);
203     }
204     
205     return lines;
206 }
207
208 static
209 gchar* parseenv (const char* string) {
210     extern char** environ;
211     gchar* newstring = g_strdup (string);
212     int i = 0;
213
214     while (environ[i] != NULL) {
215         gchar** env = g_strsplit (environ[i], "=", 0);
216         gchar* envname = g_strconcat ("$", env[0], NULL);
217
218         if (g_strrstr (newstring, envname) != NULL) {
219             newstring = str_replace(envname, env[1], newstring);
220         }
221
222         g_free (envname);
223         //g_strfreev (env); //- This still breaks uzbl, but shouldn't. The mystery thickens...
224         i ++;
225     }
226
227     return newstring;
228 }
229
230 static sigfunc*
231 setup_signal(int signr, sigfunc *shandler) {
232     struct sigaction nh, oh;
233
234     nh.sa_handler = shandler;
235     sigemptyset(&nh.sa_mask);
236     nh.sa_flags = 0;
237
238     if(sigaction(signr, &nh, &oh) < 0)
239         return SIG_ERR;
240
241     return NULL;
242 }
243
244 static void
245 clean_up(void) {
246     if (uzbl.behave.fifo_dir)
247         unlink (uzbl.comm.fifo_path);
248     if (uzbl.behave.socket_dir)
249         unlink (uzbl.comm.socket_path);
250
251     g_free(uzbl.state.executable_path);
252     g_string_free(uzbl.state.keycmd, TRUE);
253     g_hash_table_destroy(uzbl.bindings);
254     g_hash_table_destroy(uzbl.behave.commands);
255 }
256
257
258 /* --- SIGNAL HANDLER --- */
259
260 static void
261 catch_sigterm(int s) {
262     (void) s;
263     clean_up();
264 }
265
266 static void
267 catch_sigint(int s) {
268     (void) s;
269     clean_up();
270     exit(EXIT_SUCCESS);
271 }
272
273 /* --- CALLBACKS --- */
274
275 static gboolean
276 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
277     (void) web_view;
278     (void) frame;
279     (void) navigation_action;
280     (void) policy_decision;
281     (void) user_data;
282     const gchar* uri = webkit_network_request_get_uri (request);
283     if (uzbl.state.verbose)
284         printf("New window requested -> %s \n", uri);
285     new_window_load_uri(uri);
286     return (FALSE);
287 }
288
289 WebKitWebView*
290 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
291     (void) web_view;
292     (void) frame;
293     (void) user_data;
294     if (uzbl.state.selected_url != NULL) {
295         if (uzbl.state.verbose)
296             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
297         new_window_load_uri(uzbl.state.selected_url);
298     } else {
299         if (uzbl.state.verbose)
300             printf("New web view -> %s\n","Nothing to open, exiting");
301     }
302     return (NULL);
303 }
304
305 static gboolean
306 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
307     (void) web_view;
308     (void) user_data;
309     if (uzbl.behave.download_handler) {
310         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
311         if (uzbl.state.verbose)
312             printf("Download -> %s\n",uri);
313         /* if urls not escaped, we may have to escape and quote uri before this call */
314         run_handler(uzbl.behave.download_handler, uri);
315     }
316     return (FALSE);
317 }
318
319 /* scroll a bar in a given direction */
320 static void
321 scroll (GtkAdjustment* bar, GArray *argv) {
322     gdouble amount;
323     gchar *end;
324
325     amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
326     if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
327     gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
328 }
329
330 static void scroll_begin(WebKitWebView* page, GArray *argv) {
331     (void) page; (void) argv;
332     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
333 }
334
335 static void scroll_end(WebKitWebView* page, GArray *argv) {
336     (void) page; (void) argv;
337     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
338                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
339 }
340
341 static void scroll_vert(WebKitWebView* page, GArray *argv) {
342     (void) page;
343     scroll(uzbl.gui.bar_v, argv);
344 }
345
346 static void scroll_horz(WebKitWebView* page, GArray *argv) {
347     (void) page;
348     scroll(uzbl.gui.bar_h, argv);
349 }
350
351 static void
352 cmd_set_status() {
353     if (!uzbl.behave.show_status) {
354         gtk_widget_hide(uzbl.gui.mainbar);
355     } else {
356         gtk_widget_show(uzbl.gui.mainbar);
357     }
358     update_title();
359 }
360
361 static void
362 toggle_status_cb (WebKitWebView* page, GArray *argv) {
363     (void)page;
364     (void)argv;
365
366     if (uzbl.behave.show_status) {
367         gtk_widget_hide(uzbl.gui.mainbar);
368     } else {
369         gtk_widget_show(uzbl.gui.mainbar);
370     }
371     uzbl.behave.show_status = !uzbl.behave.show_status;
372     update_title();
373 }
374
375 static void
376 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
377     (void) page;
378     (void) title;
379     (void) data;
380     //Set selected_url state variable
381     g_free(uzbl.state.selected_url);
382     uzbl.state.selected_url = NULL;
383     if (link) {
384         uzbl.state.selected_url = g_strdup(link);
385     }
386     update_title();
387 }
388
389 static void
390 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
391     (void) web_view;
392     (void) web_frame;
393     (void) data;
394     if (uzbl.gui.main_title)
395         g_free (uzbl.gui.main_title);
396     uzbl.gui.main_title = g_strdup (title);
397     update_title();
398 }
399
400 static void
401 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
402     (void) page;
403     (void) data;
404     uzbl.gui.sbar.load_progress = progress;
405     update_title();
406 }
407
408 static void
409 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
410     (void) page;
411     (void) frame;
412     (void) data;
413     if (uzbl.behave.load_finish_handler)
414         run_handler(uzbl.behave.load_finish_handler, "");
415 }
416
417 static void
418 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
419     (void) page;
420     (void) frame;
421     (void) data;
422     if (uzbl.behave.load_start_handler)
423         run_handler(uzbl.behave.load_start_handler, "");
424 }
425
426 static void
427 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
428     (void) page;
429     (void) data;
430     g_free (uzbl.state.uri);
431     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
432     uzbl.state.uri = g_string_free (newuri, FALSE);
433     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
434         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
435         update_title();
436     }
437     g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
438     if (uzbl.behave.load_commit_handler)
439         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
440 }
441
442 static void
443 destroy_cb (GtkWidget* widget, gpointer data) {
444     (void) widget;
445     (void) data;
446     gtk_main_quit ();
447 }
448
449 static void
450 log_history_cb () {
451    if (uzbl.behave.history_handler) {
452        time_t rawtime;
453        struct tm * timeinfo;
454        char date [80];
455        time ( &rawtime );
456        timeinfo = localtime ( &rawtime );
457        strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
458        run_handler(uzbl.behave.history_handler, date);
459    }
460 }
461
462
463 /* VIEW funcs (little webkit wrappers) */
464 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
465 VIEWFUNC(reload)
466 VIEWFUNC(reload_bypass_cache)
467 VIEWFUNC(stop_loading)
468 VIEWFUNC(zoom_in)
469 VIEWFUNC(zoom_out)
470 VIEWFUNC(go_back)
471 VIEWFUNC(go_forward)
472 #undef VIEWFUNC
473
474 /* -- command to callback/function map for things we cannot attach to any signals */
475 // TODO: reload
476 static struct {char *name; Command command[2];} cmdlist[] =
477 {   /* key                   function      no_split      */
478     { "back",               {view_go_back, 0}              },
479     { "forward",            {view_go_forward, 0}           },
480     { "scroll_vert",        {scroll_vert, 0}               },
481     { "scroll_horz",        {scroll_horz, 0}               },
482     { "scroll_begin",       {scroll_begin, 0}              },
483     { "scroll_end",         {scroll_end, 0}                },
484     { "reload",             {view_reload, 0},              },
485     { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
486     { "stop",               {view_stop_loading, 0},        },
487     { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
488     { "zoom_out",           {view_zoom_out, 0},            },
489     { "uri",                {load_uri, NOSPLIT}            },
490     { "js",                 {run_js, NOSPLIT}              },
491     { "script",             {run_external_js, 0}           },
492     { "toggle_status",      {toggle_status_cb, 0}          },
493     { "spawn",              {spawn, 0}                     },
494     { "sh",                 {spawn_sh, 0}                  },
495     { "exit",               {close_uzbl, 0}                },
496     { "search",             {search_forward_text, NOSPLIT} },
497     { "search_reverse",     {search_reverse_text, NOSPLIT} },
498     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
499     { "runcmd",             {runcmd, NOSPLIT}              }
500 };
501
502 static void
503 commands_hash(void)
504 {
505     unsigned int i;
506     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
507
508     for (i = 0; i < LENGTH(cmdlist); i++)
509         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
510 }
511
512 /* -- CORE FUNCTIONS -- */
513
514 void
515 free_action(gpointer act) {
516     Action *action = (Action*)act;
517     g_free(action->name);
518     if (action->param)
519         g_free(action->param);
520     g_free(action);
521 }
522
523 Action*
524 new_action(const gchar *name, const gchar *param) {
525     Action *action = g_new(Action, 1);
526
527     action->name = g_strdup(name);
528     if (param)
529         action->param = g_strdup(param);
530     else
531         action->param = NULL;
532
533     return action;
534 }
535
536 static bool
537 file_exists (const char * filename) {
538     return (access(filename, F_OK) == 0);
539 }
540
541 static void
542 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
543     (void)page;
544
545     if (argv_idx(argv, 0)) {
546         if (strcmp (argv_idx(argv, 0), "0") == 0) {
547             uzbl.behave.insert_mode = FALSE;
548         } else {
549             uzbl.behave.insert_mode = TRUE;
550         }
551     } else {
552         uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
553     }
554
555     update_title();
556 }
557
558 static void
559 load_uri (WebKitWebView *web_view, GArray *argv) {
560     if (argv_idx(argv, 0)) {
561         GString* newuri = g_string_new (argv_idx(argv, 0));
562         if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
563             g_string_prepend (newuri, "http://");
564         /* if we do handle cookies, ask our handler for them */
565         webkit_web_view_load_uri (web_view, newuri->str);
566         g_string_free (newuri, TRUE);
567     }
568 }
569
570 static void
571 run_js (WebKitWebView * web_view, GArray *argv) {
572     if (argv_idx(argv, 0))
573         webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
574 }
575
576 static void
577 run_external_js (WebKitWebView * web_view, GArray *argv) {
578     if (argv_idx(argv, 0)) {
579         GArray* lines = read_file_by_line (argv_idx (argv, 0));
580         gchar*  js = NULL;
581         int i = 0;
582         gchar* line;
583
584         while ((line = g_array_index(lines, gchar*, i))) {
585             if (js == NULL) {
586                 js = g_strdup (line);
587             } else {
588                 gchar* newjs = g_strconcat (js, line, NULL);
589                 js = newjs;
590             }
591             i ++;
592             g_free (line);
593         }
594         
595         if (uzbl.state.verbose)
596             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
597
598         if (argv_idx (argv, 1)) {
599             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
600             g_free (js);
601             js = newjs;
602         }
603         webkit_web_view_execute_script (web_view, js);
604         g_free (js);
605         g_array_free (lines, TRUE);
606     }
607 }
608
609 static void
610 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
611     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
612         uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
613
614     if (uzbl.state.searchtx != NULL) {
615         if (uzbl.state.verbose)
616             printf ("Searching: %s\n", uzbl.state.searchtx);
617
618         if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
619             webkit_web_view_unmark_text_matches (page);
620             webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
621
622             if (uzbl.state.searchold != NULL)
623                 g_free (uzbl.state.searchold);
624
625             uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
626         }
627
628         webkit_web_view_set_highlight_text_matches (page, TRUE);
629         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
630         g_free(uzbl.state.searchtx);
631         uzbl.state.searchtx = NULL;
632     }
633 }
634
635 static void
636 search_forward_text (WebKitWebView *page, GArray *argv) {
637     search_text(page, argv, TRUE);
638 }
639
640 static void
641 search_reverse_text (WebKitWebView *page, GArray *argv) {
642     search_text(page, argv, FALSE);
643 }
644
645 static void
646 new_window_load_uri (const gchar * uri) {
647     GString* to_execute = g_string_new ("");
648     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
649     int i;
650     for (i = 0; entries[i].long_name != NULL; i++) {
651         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
652             gchar** str = (gchar**)entries[i].arg_data;
653             if (*str!=NULL) {
654                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
655             }
656         }
657     }
658     if (uzbl.state.verbose)
659         printf("\n%s\n", to_execute->str);
660     g_spawn_command_line_async (to_execute->str, NULL);
661     g_string_free (to_execute, TRUE);
662 }
663
664 static void
665 close_uzbl (WebKitWebView *page, GArray *argv) {
666     (void)page;
667     (void)argv;
668     gtk_main_quit ();
669 }
670
671 /* --Statusbar functions-- */
672 static char*
673 build_progressbar_ascii(int percent) {
674    int width=uzbl.gui.sbar.progress_w;
675    int i;
676    double l;
677    GString *bar = g_string_new("");
678
679    l = (double)percent*((double)width/100.);
680    l = (int)(l+.5)>=(int)l ? l+.5 : l;
681
682    for(i=0; i<(int)l; i++)
683        g_string_append(bar, uzbl.gui.sbar.progress_s);
684
685    for(; i<width; i++)
686        g_string_append(bar, uzbl.gui.sbar.progress_u);
687
688    return g_string_free(bar, FALSE);
689 }
690
691 static void
692 setup_scanner() {
693      const GScannerConfig scan_config = {
694              (
695               "\t\r\n"
696              )            /* cset_skip_characters */,
697              (
698               G_CSET_a_2_z
699               "_#"
700               G_CSET_A_2_Z
701              )            /* cset_identifier_first */,
702              (
703               G_CSET_a_2_z
704               "_0123456789"
705               G_CSET_A_2_Z
706               G_CSET_LATINS
707               G_CSET_LATINC
708              )            /* cset_identifier_nth */,
709              ( "" )    /* cpair_comment_single */,
710
711              TRUE         /* case_sensitive */,
712
713              FALSE        /* skip_comment_multi */,
714              FALSE        /* skip_comment_single */,
715              FALSE        /* scan_comment_multi */,
716              TRUE         /* scan_identifier */,
717              TRUE         /* scan_identifier_1char */,
718              FALSE        /* scan_identifier_NULL */,
719              TRUE         /* scan_symbols */,
720              FALSE        /* scan_binary */,
721              FALSE        /* scan_octal */,
722              FALSE        /* scan_float */,
723              FALSE        /* scan_hex */,
724              FALSE        /* scan_hex_dollar */,
725              FALSE        /* scan_string_sq */,
726              FALSE        /* scan_string_dq */,
727              TRUE         /* numbers_2_int */,
728              FALSE        /* int_2_float */,
729              FALSE        /* identifier_2_string */,
730              FALSE        /* char_2_token */,
731              FALSE        /* symbol_2_token */,
732              TRUE         /* scope_0_fallback */,
733              FALSE,
734              TRUE
735      };
736
737      uzbl.scan = g_scanner_new(&scan_config);
738      while(symp->symbol_name) {
739          g_scanner_scope_add_symbol(uzbl.scan, 0,
740                          symp->symbol_name,
741                          GINT_TO_POINTER(symp->symbol_token));
742          symp++;
743      }
744 }
745
746 static gchar *
747 expand_template(const char *template) {
748      if(!template) return NULL;
749
750      GTokenType token = G_TOKEN_NONE;
751      GString *ret = g_string_new("");
752      char *buf=NULL;
753      int sym;
754
755      g_scanner_input_text(uzbl.scan, template, strlen(template));
756      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
757          token = g_scanner_get_next_token(uzbl.scan);
758
759          if(token == G_TOKEN_SYMBOL) {
760              sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
761              switch(sym) {
762                  case SYM_URI:
763                      buf = uzbl.state.uri?
764                          g_markup_printf_escaped("%s", uzbl.state.uri) :
765                          g_strdup("");
766                      g_string_append(ret, buf);
767                      g_free(buf);
768                      break;
769                  case SYM_LOADPRGS:
770                      buf = itos(uzbl.gui.sbar.load_progress);
771                      g_string_append(ret, buf);
772                      g_free(buf);
773                      break;
774                  case SYM_LOADPRGSBAR:
775                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
776                      g_string_append(ret, buf);
777                      g_free(buf);
778                      break;
779                  case SYM_TITLE:
780                      buf = uzbl.gui.main_title?
781                          g_markup_printf_escaped("%s", uzbl.gui.main_title) :
782                          g_strdup("");
783                      g_string_append(ret, buf);
784                      g_free(buf);
785                      break;
786                  case SYM_SELECTED_URI:
787                      buf = uzbl.state.selected_url?
788                          g_markup_printf_escaped("%s", uzbl.state.selected_url) :
789                          g_strdup("");
790                      g_string_append(ret, buf);
791                      g_free(buf);
792                     break;
793                  case SYM_NAME:
794                      buf = itos(uzbl.xwin);
795                      g_string_append(ret,
796                          uzbl.state.instance_name?uzbl.state.instance_name:buf);
797                      g_free(buf);
798                      break;
799                  case SYM_KEYCMD:
800                      buf = uzbl.state.keycmd->str?
801                          g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
802                          g_strdup("");
803                      g_string_append(ret, buf);
804                      g_free(buf);
805                      break;
806                  case SYM_MODE:
807                      g_string_append(ret,
808                          uzbl.behave.insert_mode?"[I]":"[C]");
809                      break;
810                  case SYM_MSG:
811                      g_string_append(ret,
812                          uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
813                      break;
814                      /* useragent syms */
815                  case SYM_WK_MAJ:
816                      buf = itos(WEBKIT_MAJOR_VERSION);
817                      g_string_append(ret, buf);
818                      g_free(buf);
819                      break;
820                  case SYM_WK_MIN:
821                      buf = itos(WEBKIT_MINOR_VERSION);
822                      g_string_append(ret, buf);
823                      g_free(buf);
824                      break;
825                  case SYM_WK_MIC:
826                      buf = itos(WEBKIT_MICRO_VERSION);
827                      g_string_append(ret, buf);
828                      g_free(buf);
829                      break;
830                  case SYM_SYSNAME:
831                      g_string_append(ret, uzbl.state.unameinfo.sysname);
832                      break;
833                  case SYM_NODENAME:
834                      g_string_append(ret, uzbl.state.unameinfo.nodename);
835                      break;
836                  case SYM_KERNREL:
837                      g_string_append(ret, uzbl.state.unameinfo.release);
838                      break;
839                  case SYM_KERNVER:
840                      g_string_append(ret, uzbl.state.unameinfo.version);
841                      break;
842                  case SYM_ARCHSYS:
843                      g_string_append(ret, uzbl.state.unameinfo.machine);
844                      break;
845                  case SYM_ARCHUZBL:
846                      g_string_append(ret, ARCH);
847                      break;
848 #ifdef _GNU_SOURCE
849                  case SYM_DOMAINNAME:
850                      g_string_append(ret, uzbl.state.unameinfo.domainname);
851                      break;
852 #endif
853                  case SYM_COMMIT:
854                      g_string_append(ret, COMMIT);
855                      break;
856                  default:
857                      break;
858              }
859          }
860          else if(token == G_TOKEN_INT) {
861              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
862              g_string_append(ret, buf);
863              g_free(buf);
864          }
865          else if(token == G_TOKEN_IDENTIFIER) {
866              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
867          }
868          else if(token == G_TOKEN_CHAR) {
869              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
870          }
871      }
872
873      return g_string_free(ret, FALSE);
874 }
875 /* --End Statusbar functions-- */
876
877 static void
878 sharg_append(GArray *a, const gchar *str) {
879     const gchar *s = (str ? str : "");
880     g_array_append_val(a, s);
881 }
882
883 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
884 static gboolean
885 run_command (const gchar *command, const guint npre, const gchar **args,
886              const gboolean sync, char **stdout) {
887    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
888     GError *err = NULL;
889     
890     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
891     gchar *pid = itos(getpid());
892     gchar *xwin = itos(uzbl.xwin);
893     guint i;
894     sharg_append(a, command);
895     for (i = 0; i < npre; i++) /* add n args before the default vars */
896         sharg_append(a, args[i]);
897     sharg_append(a, uzbl.state.config_file);
898     sharg_append(a, pid);
899     sharg_append(a, xwin);
900     sharg_append(a, uzbl.comm.fifo_path);
901     sharg_append(a, uzbl.comm.socket_path);
902     sharg_append(a, uzbl.state.uri);
903     sharg_append(a, uzbl.gui.main_title);
904
905     for (i = npre; i < g_strv_length((gchar**)args); i++)
906         sharg_append(a, args[i]);
907     
908     gboolean result;
909     if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
910                                     NULL, NULL, stdout, NULL, NULL, &err);
911     else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
912                                 NULL, NULL, NULL, &err);
913
914     if (uzbl.state.verbose) {
915         GString *s = g_string_new("spawned:");
916         for (i = 0; i < (a->len); i++) {
917             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
918             g_string_append_printf(s, " %s", qarg);
919             g_free (qarg);
920         }
921         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
922         printf("%s\n", s->str);
923         g_string_free(s, TRUE);
924     }
925     if (err) {
926         g_printerr("error on run_command: %s\n", err->message);
927         g_error_free (err);
928     }
929     g_free (pid);
930     g_free (xwin);
931     g_array_free (a, TRUE);
932     return result;
933 }
934
935 static gchar**
936 split_quoted(const gchar* src, const gboolean unquote) {
937     /* split on unquoted space, return array of strings;
938        remove a layer of quotes and backslashes if unquote */
939     if (!src) return NULL;
940     
941     gboolean dq = FALSE;
942     gboolean sq = FALSE;
943     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
944     GString *s = g_string_new ("");
945     const gchar *p;
946     gchar **ret;
947     gchar *dup;
948     for (p = src; *p != '\0'; p++) {
949         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
950         else if (*p == '\\') { g_string_append_c(s, *p++);
951                                g_string_append_c(s, *p); }
952         else if ((*p == '"') && unquote && !sq) dq = !dq;
953         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
954                                      dq = !dq; }
955         else if ((*p == '\'') && unquote && !dq) sq = !sq;
956         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
957                                       sq = ! sq; }
958         else if ((*p == ' ') && !dq && !sq) {
959             dup = g_strdup(s->str);
960             g_array_append_val(a, dup);
961             g_string_truncate(s, 0);
962         } else g_string_append_c(s, *p);
963     }
964     dup = g_strdup(s->str);
965     g_array_append_val(a, dup);
966     ret = (gchar**)a->data;
967     g_array_free (a, FALSE);
968     g_string_free (s, TRUE);
969     return ret;
970 }
971
972 static void
973 spawn(WebKitWebView *web_view, GArray *argv) {
974     (void)web_view;
975     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
976     if (argv_idx(argv, 0))
977         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
978 }
979
980 static void
981 spawn_sh(WebKitWebView *web_view, GArray *argv) {
982     (void)web_view;
983     if (!uzbl.behave.shell_cmd) {
984         g_printerr ("spawn_sh: shell_cmd is not set!\n");
985         return;
986     }
987     
988     guint i;
989     gchar *spacer = g_strdup("");
990     g_array_insert_val(argv, 1, spacer);
991     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
992
993     for (i = 1; i < g_strv_length(cmd); i++)
994         g_array_prepend_val(argv, cmd[i]);
995
996     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
997     g_free (spacer);
998     g_strfreev (cmd);
999 }
1000
1001 static void
1002 parse_command(const char *cmd, const char *param) {
1003     Command *c;
1004
1005     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1006
1007             guint i;
1008             gchar **par = split_quoted(param, TRUE);
1009             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1010
1011             if (c[1] == NOSPLIT) { /* don't split */
1012                 sharg_append(a, param);
1013             } else if (par) {
1014                 for (i = 0; i < g_strv_length(par); i++)
1015                     sharg_append(a, par[i]);
1016             }
1017             c[0](uzbl.gui.web_view, a);
1018             g_strfreev (par);
1019             g_array_free (a, TRUE);
1020
1021     } else
1022         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1023 }
1024
1025 /* command parser */
1026 static void
1027 setup_regex() {
1028     uzbl.comm.get_regex  = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1029             G_REGEX_OPTIMIZE, 0, NULL);
1030     uzbl.comm.set_regex  = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1031             G_REGEX_OPTIMIZE, 0, NULL);
1032     uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1033             G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1034     uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1035             G_REGEX_OPTIMIZE, 0, NULL);
1036     uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1037             G_REGEX_OPTIMIZE, 0, NULL);
1038 }
1039
1040 static gboolean
1041 get_var_value(gchar *name) {
1042     uzbl_cmdprop *c;
1043
1044     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1045         if(c->type == TYPE_STRING)
1046             printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1047         else if(c->type == TYPE_INT)
1048             printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1049     }
1050     return TRUE;
1051 }
1052
1053 static void
1054 set_proxy_url() {
1055     SoupURI *suri;
1056
1057     if(*uzbl.net.proxy_url == ' '
1058        || uzbl.net.proxy_url == NULL) {
1059         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1060                 (GType) SOUP_SESSION_PROXY_URI);
1061     }
1062     else {
1063         suri = soup_uri_new(uzbl.net.proxy_url);
1064         g_object_set(G_OBJECT(uzbl.net.soup_session),
1065                 SOUP_SESSION_PROXY_URI,
1066                 suri, NULL);
1067         soup_uri_free(suri);
1068     }
1069     return;
1070 }
1071
1072 static void
1073 cmd_load_uri() {
1074     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1075     g_array_append_val (a, uzbl.state.uri);
1076     load_uri(uzbl.gui.web_view, a);
1077     g_array_free (a, TRUE);
1078 }
1079
1080 static void 
1081 cmd_always_insert_mode() {
1082     uzbl.behave.insert_mode =
1083         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1084     update_title();
1085 }
1086
1087 static void
1088 cmd_max_conns() {
1089     g_object_set(G_OBJECT(uzbl.net.soup_session),
1090             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1091 }
1092
1093 static void
1094 cmd_max_conns_host() {
1095     g_object_set(G_OBJECT(uzbl.net.soup_session),
1096             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1097 }
1098
1099 static void
1100 cmd_http_debug() {
1101     soup_session_remove_feature
1102         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1103     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1104     /*g_free(uzbl.net.soup_logger);*/
1105
1106     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1107     soup_session_add_feature(uzbl.net.soup_session,
1108             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1109 }
1110
1111 static void
1112 cmd_default_font_size() {
1113     WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1114     g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1115 }
1116
1117 static void
1118 cmd_minimum_font_size() {
1119     WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1120     g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1121 }
1122
1123 static void
1124 cmd_fifo_dir() {
1125     char *buf;
1126
1127     buf = init_fifo(uzbl.behave.fifo_dir);
1128     if(uzbl.behave.fifo_dir) 
1129         g_free(uzbl.behave.fifo_dir);
1130
1131     uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1132 }
1133
1134 static void
1135 cmd_socket_dir() {
1136     char *buf;
1137
1138     buf = init_socket(uzbl.behave.socket_dir);
1139     if(uzbl.behave.socket_dir) 
1140         g_free(uzbl.behave.socket_dir);
1141
1142     uzbl.behave.socket_dir = buf?buf:g_strdup("");
1143 }
1144
1145 static void
1146 cmd_modkey() {
1147     int i;
1148     char *buf;
1149
1150     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1151     uzbl.behave.modmask = 0;
1152
1153     if(uzbl.behave.modkey) 
1154         g_free(uzbl.behave.modkey);
1155     uzbl.behave.modkey = buf;
1156
1157     for (i = 0; modkeys[i].key != NULL; i++) {
1158         if (g_strrstr(buf, modkeys[i].key))
1159             uzbl.behave.modmask |= modkeys[i].mask;
1160     }
1161 }
1162
1163 static void
1164 cmd_useragent() {
1165     char *buf;
1166
1167     buf = set_useragent(uzbl.net.useragent);
1168     if(uzbl.net.useragent) 
1169         g_free(uzbl.net.useragent);
1170
1171     uzbl.net.useragent = buf?buf:g_strdup("");
1172 }
1173
1174 static void
1175 move_statusbar() {
1176     gtk_widget_ref(uzbl.gui.scrolled_win);
1177     gtk_widget_ref(uzbl.gui.mainbar);
1178     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1179     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1180
1181     if(uzbl.behave.status_top) {
1182         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1183         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1184     }
1185     else {
1186         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1187         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1188     }
1189     gtk_widget_unref(uzbl.gui.scrolled_win);
1190     gtk_widget_unref(uzbl.gui.mainbar);
1191     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1192     return;
1193 }
1194
1195 static gboolean
1196 set_var_value(gchar *name, gchar *val) {
1197     uzbl_cmdprop *c = NULL;
1198     char *endp = NULL;
1199
1200     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1201         /* check for the variable type */
1202         if (c->type == TYPE_STRING) {
1203             g_free(*c->ptr);
1204             *c->ptr = g_strdup(val);
1205         } else if(c->type == TYPE_INT) {
1206             int *ip = GPOINTER_TO_INT(c->ptr);
1207             *ip = (int)strtoul(val, &endp, 10);
1208         }
1209
1210         /* invoke a command specific function */
1211         if(c->func) c->func();
1212     }
1213     return TRUE;
1214 }
1215
1216 static void
1217 runcmd(WebKitWebView* page, GArray *argv) {
1218     (void) page;
1219     parse_cmd_line(argv_idx(argv, 0));
1220 }
1221
1222 static void
1223 parse_cmd_line(const char *ctl_line) {
1224     gchar **tokens;
1225
1226     /* SET command */
1227     if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1228         tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1229         if(tokens[0][0] == 0) {
1230             gchar* value = parseenv (tokens[2]);
1231             set_var_value(tokens[1], value);
1232             g_strfreev(tokens);
1233             g_free(value);
1234         }
1235         else
1236             printf("Error in command: %s\n", tokens[0]);
1237     }
1238     /* GET command */
1239     else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1240         tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1241         if(tokens[0][0] == 0) {
1242             get_var_value(tokens[1]);
1243             g_strfreev(tokens);
1244         }
1245         else
1246             printf("Error in command: %s\n", tokens[0]);
1247     }
1248     /* BIND command */
1249     else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1250         tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1251         if(tokens[0][0] == 0) {
1252             gchar* value = parseenv (tokens[2]);
1253             add_binding(tokens[1], value);
1254             g_strfreev(tokens);
1255             g_free(value);
1256         }
1257         else
1258             printf("Error in command: %s\n", tokens[0]);
1259     }
1260     /* ACT command */
1261     else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1262         tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1263         if(tokens[0][0] == 0) {
1264             parse_command(tokens[1], tokens[2]);
1265             g_strfreev(tokens);
1266         }
1267         else
1268             printf("Error in command: %s\n", tokens[0]);
1269     }
1270     /* KEYCMD command */
1271     else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1272         tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1273         if(tokens[0][0] == 0) {
1274             /* should incremental commands want each individual "keystroke"
1275                sent in a loop or the whole string in one go like now? */
1276             g_string_assign(uzbl.state.keycmd, tokens[1]);
1277             run_keycmd(FALSE);
1278             if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1279                 run_keycmd(TRUE);
1280             update_title();
1281             g_strfreev(tokens);
1282         }
1283     }
1284     /* Comments */
1285     else if(   (ctl_line[0] == '#')
1286             || (ctl_line[0] == ' ')
1287             || (ctl_line[0] == '\n'))
1288         ; /* ignore these lines */
1289     else
1290         printf("Command not understood (%s)\n", ctl_line);
1291
1292     return;
1293 }
1294
1295 static gchar*
1296 build_stream_name(int type, const gchar* dir) {
1297     char *xwin_str;
1298     State *s = &uzbl.state;
1299     gchar *str;
1300
1301     xwin_str = itos((int)uzbl.xwin);
1302     if (type == FIFO) {
1303         str = g_strdup_printf
1304             ("%s/uzbl_fifo_%s", dir,
1305              s->instance_name ? s->instance_name : xwin_str);
1306     } else if (type == SOCKET) {
1307         str = g_strdup_printf
1308             ("%s/uzbl_socket_%s", dir,
1309              s->instance_name ? s->instance_name : xwin_str );
1310     }
1311     g_free(xwin_str);
1312     return str;
1313 }
1314
1315 static gboolean
1316 control_fifo(GIOChannel *gio, GIOCondition condition) {
1317     if (uzbl.state.verbose)
1318         printf("triggered\n");
1319     gchar *ctl_line;
1320     GIOStatus ret;
1321     GError *err = NULL;
1322
1323     if (condition & G_IO_HUP)
1324         g_error ("Fifo: Read end of pipe died!\n");
1325
1326     if(!gio)
1327        g_error ("Fifo: GIOChannel broke\n");
1328
1329     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1330     if (ret == G_IO_STATUS_ERROR) {
1331         g_error ("Fifo: Error reading: %s\n", err->message);
1332         g_error_free (err);
1333     }
1334
1335     parse_cmd_line(ctl_line);
1336     g_free(ctl_line);
1337
1338     return TRUE;
1339 }
1340
1341 static gchar*
1342 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1343     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1344         if (unlink(uzbl.comm.fifo_path) == -1)
1345             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1346         g_free(uzbl.comm.fifo_path);
1347         uzbl.comm.fifo_path = NULL;
1348     }
1349
1350     if (*dir == ' ') { /* space unsets the variable */
1351         return NULL;
1352     }
1353
1354     GIOChannel *chan = NULL;
1355     GError *error = NULL;
1356     gchar *path = build_stream_name(FIFO, dir);
1357
1358     if (!file_exists(path)) {
1359         if (mkfifo (path, 0666) == 0) {
1360             // 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.
1361             chan = g_io_channel_new_file(path, "r+", &error);
1362             if (chan) {
1363                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1364                     if (uzbl.state.verbose)
1365                         printf ("init_fifo: created successfully as %s\n", path);
1366                     uzbl.comm.fifo_path = path;
1367                     return dir;
1368                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1369             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1370         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1371     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1372
1373     /* if we got this far, there was an error; cleanup */
1374     if (error) g_error_free (error);
1375     g_free(path);
1376     return NULL;
1377 }
1378
1379 static gboolean
1380 control_stdin(GIOChannel *gio, GIOCondition condition) {
1381     (void) condition;
1382     gchar *ctl_line = NULL;
1383     GIOStatus ret;
1384
1385     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1386     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1387         return FALSE;
1388
1389     parse_cmd_line(ctl_line);
1390     g_free(ctl_line);
1391
1392     return TRUE;
1393 }
1394
1395 static void
1396 create_stdin () {
1397     GIOChannel *chan = NULL;
1398     GError *error = NULL;
1399
1400     chan = g_io_channel_unix_new(fileno(stdin));
1401     if (chan) {
1402         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1403             g_error ("Stdin: could not add watch\n");
1404         } else {
1405             if (uzbl.state.verbose)
1406                 printf ("Stdin: watch added successfully\n");
1407         }
1408     } else {
1409         g_error ("Stdin: Error while opening: %s\n", error->message);
1410     }
1411     if (error) g_error_free (error);
1412 }
1413
1414 static gboolean
1415 control_socket(GIOChannel *chan) {
1416     struct sockaddr_un remote;
1417     char buffer[512], *ctl_line;
1418     char temp[128];
1419     int sock, clientsock, n, done;
1420     unsigned int t;
1421
1422     sock = g_io_channel_unix_get_fd(chan);
1423
1424     memset (buffer, 0, sizeof (buffer));
1425
1426     t          = sizeof (remote);
1427     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1428
1429     done = 0;
1430     do {
1431         memset (temp, 0, sizeof (temp));
1432         n = recv (clientsock, temp, 128, 0);
1433         if (n == 0) {
1434             buffer[strlen (buffer)] = '\0';
1435             done = 1;
1436         }
1437         if (!done)
1438             strcat (buffer, temp);
1439     } while (!done);
1440
1441     if (strcmp (buffer, "\n") < 0) {
1442         buffer[strlen (buffer) - 1] = '\0';
1443     } else {
1444         buffer[strlen (buffer)] = '\0';
1445     }
1446     close (clientsock);
1447     ctl_line = g_strdup(buffer);
1448     parse_cmd_line (ctl_line);
1449
1450 /*
1451    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1452     GError *error = NULL;
1453     gsize len;
1454     GIOStatus ret;
1455     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1456     if (ret == G_IO_STATUS_ERROR)
1457         g_error ("Error reading: %s\n", error->message);
1458
1459     printf("Got line %s (%u bytes) \n",ctl_line, len);
1460     if(ctl_line) {
1461        parse_line(ctl_line);
1462 */
1463
1464     g_free(ctl_line);
1465     return TRUE;
1466 }
1467
1468 static gchar*
1469 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1470     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1471         if (unlink(uzbl.comm.socket_path) == -1)
1472             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1473         g_free(uzbl.comm.socket_path);
1474         uzbl.comm.socket_path = NULL;
1475     }
1476
1477     if (*dir == ' ') {
1478         g_free(dir);
1479         return NULL;
1480     }
1481
1482     GIOChannel *chan = NULL;
1483     int sock, len;
1484     struct sockaddr_un local;
1485     gchar *path = build_stream_name(SOCKET, dir);
1486
1487     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1488
1489     local.sun_family = AF_UNIX;
1490     strcpy (local.sun_path, path);
1491     unlink (local.sun_path);
1492
1493     len = strlen (local.sun_path) + sizeof (local.sun_family);
1494     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1495         if (uzbl.state.verbose)
1496             printf ("init_socket: opened in %s\n", path);
1497         listen (sock, 5);
1498
1499         if( (chan = g_io_channel_unix_new(sock)) ) {
1500             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1501             uzbl.comm.socket_path = path;
1502             return dir;
1503         }
1504     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1505
1506     /* if we got this far, there was an error; cleanup */
1507     g_free(path);
1508     g_free(dir);
1509     return NULL;
1510 }
1511
1512 /*
1513  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1514  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1515 */
1516 // this function may be called very early when the templates are not set (yet), hence the checks
1517 static void
1518 update_title (void) {
1519     Behaviour *b = &uzbl.behave;
1520     gchar *parsed;
1521
1522     if (b->show_status) {
1523         if (b->title_format_short) {
1524             parsed = expand_template(b->title_format_short);
1525             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1526             g_free(parsed);
1527         }
1528         if (b->status_format) {
1529             parsed = expand_template(b->status_format);
1530             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1531             g_free(parsed);
1532         }
1533         if (b->status_background) {
1534             GdkColor color;
1535             gdk_color_parse (b->status_background, &color);
1536             //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)
1537             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1538         }
1539     } else {
1540         if (b->title_format_long) {
1541             parsed = expand_template(b->title_format_long);
1542             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1543             g_free(parsed);
1544         }
1545     }
1546 }
1547
1548 static gboolean
1549 key_press_cb (GtkWidget* window, GdkEventKey* event)
1550 {
1551     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1552
1553     (void) window;
1554
1555     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1556         || 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)
1557         return FALSE;
1558
1559     /* turn off insert mode (if always_insert_mode is not used) */
1560     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1561         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1562         update_title();
1563         return TRUE;
1564     }
1565
1566     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1567         return FALSE;
1568
1569     if (event->keyval == GDK_Escape) {
1570         g_string_truncate(uzbl.state.keycmd, 0);
1571         update_title();
1572         return TRUE;
1573     }
1574
1575     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1576     if (event->keyval == GDK_Insert) {
1577         gchar * str;
1578         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1579             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1580         } else {
1581             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1582         }
1583         if (str) {
1584             g_string_append (uzbl.state.keycmd, str);
1585             update_title ();
1586             g_free (str);
1587         }
1588         return TRUE;
1589     }
1590
1591     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1592         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1593         update_title();
1594     }
1595
1596     gboolean key_ret = FALSE;
1597     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1598         key_ret = TRUE;
1599     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1600
1601     run_keycmd(key_ret);
1602     update_title();
1603     if (key_ret) return (!uzbl.behave.insert_mode);
1604     return TRUE;
1605 }
1606
1607 static void
1608 run_keycmd(const gboolean key_ret) {
1609     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1610     Action *action;
1611     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1612         g_string_truncate(uzbl.state.keycmd, 0);
1613         parse_command(action->name, action->param);
1614         return;
1615     }
1616
1617     /* try if it's an incremental keycmd or one that takes args, and run it */
1618     GString* short_keys = g_string_new ("");
1619     GString* short_keys_inc = g_string_new ("");
1620     unsigned int i;
1621     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1622         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1623         g_string_assign(short_keys_inc, short_keys->str);
1624         g_string_append_c(short_keys, '_');
1625         g_string_append_c(short_keys_inc, '*');
1626
1627         gboolean exec_now = FALSE;
1628         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1629             if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1630         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1631             if (key_ret) { /* just quit the incremental command on return */
1632                 g_string_truncate(uzbl.state.keycmd, 0);
1633                 break;
1634             } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1635         }
1636
1637         if (exec_now) {
1638             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1639             GString* actionname = g_string_new ("");
1640             GString* actionparam = g_string_new ("");
1641             g_string_erase (parampart, 0, i+1);
1642             if (action->name)
1643                 g_string_printf (actionname, action->name, parampart->str);
1644             if (action->param)
1645                 g_string_printf (actionparam, action->param, parampart->str);
1646             parse_command(actionname->str, actionparam->str);
1647             g_string_free (actionname, TRUE);
1648             g_string_free (actionparam, TRUE);
1649             g_string_free (parampart, TRUE);
1650             if (key_ret)
1651                 g_string_truncate(uzbl.state.keycmd, 0);
1652             break;
1653         }
1654
1655         g_string_truncate(short_keys, short_keys->len - 1);
1656     }
1657     g_string_free (short_keys, TRUE);
1658     g_string_free (short_keys_inc, TRUE);
1659 }
1660
1661 static GtkWidget*
1662 create_browser () {
1663     GUI *g = &uzbl.gui;
1664
1665     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1666     //main_window_ref = g_object_ref(scrolled_window);
1667     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
1668
1669     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1670     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1671
1672     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1673     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1674     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1675     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1676     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1677     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1678     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1679     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1680     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1681     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1682
1683     return scrolled_window;
1684 }
1685
1686 static GtkWidget*
1687 create_mainbar () {
1688     GUI *g = &uzbl.gui;
1689
1690     g->mainbar = gtk_hbox_new (FALSE, 0);
1691
1692     /* keep a reference to the bar so we can re-pack it at runtime*/
1693     //sbar_ref = g_object_ref(g->mainbar);
1694
1695     g->mainbar_label = gtk_label_new ("");
1696     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1697     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1698     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1699     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1700     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1701     return g->mainbar;
1702 }
1703
1704 static
1705 GtkWidget* create_window () {
1706     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1707     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1708     gtk_widget_set_name (window, "Uzbl browser");
1709     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1710     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1711
1712     return window;
1713 }
1714
1715 static void
1716 run_handler (const gchar *act, const gchar *args) {
1717     char **parts = g_strsplit(act, " ", 2);
1718     if (!parts) return;
1719     else if ((g_strcmp0(parts[0], "spawn") == 0)
1720              || (g_strcmp0(parts[0], "sh") == 0)) {
1721         guint i;
1722         GString *a = g_string_new ("");
1723         char **spawnparts;
1724         spawnparts = split_quoted(parts[1], FALSE);
1725         g_string_append_printf(a, "%s", spawnparts[0]);
1726         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1727         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1728             g_string_append_printf(a, " %s", spawnparts[i]);
1729         parse_command(parts[0], a->str);
1730         g_string_free (a, TRUE);
1731         g_strfreev (spawnparts);
1732     } else
1733         parse_command(parts[0], parts[1]);
1734     g_strfreev (parts);
1735 }
1736
1737 static void
1738 add_binding (const gchar *key, const gchar *act) {
1739     char **parts = g_strsplit(act, " ", 2);
1740     Action *action;
1741
1742     if (!parts)
1743         return;
1744
1745     //Debug:
1746     if (uzbl.state.verbose)
1747         printf ("Binding %-10s : %s\n", key, act);
1748     action = new_action(parts[0], parts[1]);
1749
1750     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1751     g_strfreev(parts);
1752 }
1753
1754 static gchar*
1755 get_xdg_var (XDG_Var xdg) {
1756     const gchar* actual_value = getenv (xdg.environmental);
1757     const gchar* home         = getenv ("HOME");
1758
1759     gchar* return_value = str_replace ("~", home, actual_value);
1760
1761     if (! actual_value || strcmp (actual_value, "") == 0) {
1762         if (xdg.default_value) {
1763             return_value = str_replace ("~", home, xdg.default_value);
1764         } else {
1765             return_value = NULL;
1766         }
1767     }
1768     return return_value;
1769 }
1770
1771 static gchar*
1772 find_xdg_file (int xdg_type, char* filename) {
1773     /* xdg_type = 0 => config
1774        xdg_type = 1 => data
1775        xdg_type = 2 => cache*/
1776
1777     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1778     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1779     g_free (xdgv);
1780
1781     gchar* temporary_string;
1782     char*  saveptr;
1783     char*  buf;
1784
1785     if (! file_exists (temporary_file) && xdg_type != 2) {
1786         buf = get_xdg_var (XDG[3 + xdg_type]);
1787         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1788         g_free(buf);
1789
1790         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1791             g_free (temporary_file);
1792             temporary_file = g_strconcat (temporary_string, filename, NULL);
1793         }
1794     }
1795     
1796     //g_free (temporary_string); - segfaults.
1797
1798     if (file_exists (temporary_file)) {
1799         return temporary_file;
1800     } else {
1801         return NULL;
1802     }
1803 }
1804 static void
1805 settings_init () {
1806     State *s = &uzbl.state;
1807     Network *n = &uzbl.net;
1808     int i;
1809     for (i = 0; default_config[i].command != NULL; i++) {
1810         parse_cmd_line(default_config[i].command);
1811     }
1812
1813     if (!s->config_file) {
1814         s->config_file = find_xdg_file (0, "/uzbl/config");
1815     }
1816
1817     if (s->config_file) {
1818         GArray* lines = read_file_by_line (s->config_file);
1819         int i = 0;
1820         gchar* line;
1821
1822         while ((line = g_array_index(lines, gchar*, i))) {
1823             parse_cmd_line (line);
1824             i ++;
1825             g_free (line);
1826         }
1827         g_array_free (lines, TRUE);
1828     } else {
1829         if (uzbl.state.verbose)
1830             printf ("No configuration file loaded.\n");
1831     }
1832
1833     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1834 }
1835
1836 static gchar*
1837 set_useragent(gchar *val) {
1838     if (*val == ' ') {
1839         g_free(val);
1840         return NULL;
1841     }
1842     gchar *ua = expand_template(val);
1843     if (ua)
1844         g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1845     return ua;
1846 }
1847
1848 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1849     (void) session;
1850     (void) user_data;
1851     if (!uzbl.behave.cookie_handler) return;
1852
1853     gchar * stdout = NULL;
1854     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1855     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1856     gchar *action = g_strdup ("GET");
1857     SoupURI * soup_uri = soup_message_get_uri(msg);
1858     sharg_append(a, action);
1859     sharg_append(a, soup_uri->host);
1860     sharg_append(a, soup_uri->path);
1861     run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1862     //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1863     if(stdout) {
1864         soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1865     }
1866     g_free (action);
1867     g_array_free(a, TRUE);
1868 }
1869
1870 static void
1871 save_cookies (SoupMessage *msg, gpointer user_data){
1872     (void) user_data;
1873     GSList *ck;
1874     char *cookie;
1875     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1876         cookie = soup_cookie_to_set_cookie_header(ck->data);
1877         GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1878         SoupURI * soup_uri = soup_message_get_uri(msg);
1879         gchar *action = strdup("PUT");
1880         sharg_append(a, action);
1881         sharg_append(a, soup_uri->host);
1882         sharg_append(a, soup_uri->path);
1883         sharg_append(a, cookie);
1884         run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1885         g_free (cookie);
1886         g_free (action);
1887         g_array_free(a, TRUE);
1888     }
1889     g_slist_free(ck);
1890 }
1891
1892 int
1893 main (int argc, char* argv[]) {
1894     gtk_init (&argc, &argv);
1895     if (!g_thread_supported ())
1896         g_thread_init (NULL);
1897     uzbl.state.executable_path = g_strdup(argv[0]);
1898     uzbl.state.selected_url = NULL;
1899     uzbl.state.searchtx = NULL;
1900
1901     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1902     g_option_context_add_main_entries (context, entries, NULL);
1903     g_option_context_add_group (context, gtk_get_option_group (TRUE));
1904     g_option_context_parse (context, &argc, &argv, NULL);
1905     g_option_context_free(context);
1906     /* initialize hash table */
1907     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1908
1909     uzbl.net.soup_session = webkit_get_default_session();
1910     uzbl.state.keycmd = g_string_new("");
1911
1912     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1913         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1914     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1915         fprintf(stderr, "uzbl: error hooking SIGINT\n");
1916
1917     if(uname(&uzbl.state.unameinfo) == -1)
1918         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
1919
1920     uzbl.gui.sbar.progress_s = g_strdup("=");
1921     uzbl.gui.sbar.progress_u = g_strdup("ยท");
1922     uzbl.gui.sbar.progress_w = 10;
1923
1924     setup_regex();
1925     setup_scanner();
1926     commands_hash ();
1927     make_var_to_name_hash();
1928
1929     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1930
1931     uzbl.gui.scrolled_win = create_browser();
1932     create_mainbar();
1933
1934     /* initial packing */
1935     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1936     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1937
1938     uzbl.gui.main_window = create_window ();
1939     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1940
1941
1942     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1943     gtk_widget_show_all (uzbl.gui.main_window);
1944     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1945
1946     if (uzbl.state.verbose) {
1947         printf("Uzbl start location: %s\n", argv[0]);
1948         printf("window_id %i\n",(int) uzbl.xwin);
1949         printf("pid %i\n", getpid ());
1950         printf("name: %s\n", uzbl.state.instance_name);
1951     }
1952
1953     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1954     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1955     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1956     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1957     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1958
1959     settings_init ();
1960
1961     if (!uzbl.behave.show_status)
1962         gtk_widget_hide(uzbl.gui.mainbar);
1963     else
1964         update_title();
1965
1966     create_stdin();
1967
1968     if(uzbl.state.uri) {
1969         GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1970         g_array_append_val(a, uzbl.state.uri);
1971         load_uri (uzbl.gui.web_view, a);
1972         g_array_free (a, TRUE);
1973     }
1974
1975     gtk_main ();
1976     clean_up();
1977
1978     return EXIT_SUCCESS;
1979 }
1980
1981 /* vi: set et ts=4: */