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